zoukankan      html  css  js  c++  java
  • 单点登录

    传统登陆的方式:

    1.登陆时,进行验证,验证完毕后将用户信息存储到session

        /**
         * 用户登录
         * @param user
         * @return
         * HttpSession因为在Survey_2_Component中使用,
         * 所以在Survey_2_Component工程中加入JSP-API的依赖后才能使用
         */
        @RequestMapping(value="/guest/user/login",method=RequestMethod.POST)
        public String userLogin(User user,HttpSession httpSession){
            User queryUser = userService.login(user);
            //将带有id的用户数据存进session域
            httpSession.setAttribute(GlobalNames.LOGIN_USER, queryUser);
            return "redirect:/index.jsp";
        }

     2.其它系统的登陆验证

    用拦截器判断用户是否登陆

    public class AuthorityCheckInterceptor  implements HandlerInterceptor{
        @Autowired
        private ResService resService;
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
            //1.放行静态资源
            if(handler instanceof DefaultServletHttpRequestHandler){
                return true;
            }
            
            //6.如果不是公共资源,检查登录状态——区分guest/manager
            HttpSession session = request.getSession();
            if(path.startsWith("/guest")){
                User user = (User)session.getAttribute(GlobalNames.LOGIN_USER);
                if(user==null){
                    throw new AdminAccessForbiddenException(GlobalMessage.ADMIN_ACCESS_FORBIDDEN);
                }
                .....
            }
            
    }

    此方式在只有一个web工程时是没有问题。

    jsp的四个作用域page、request、session、application的作用范围

    都是同一个web工程中:

     page:用户请求的当前页面;

      Request:可以通过转发来进行传递。用户请求访问的当前组件,以及和当前web组件共享同一用户请求的web组件。

          如:被请求的jsp页面和该页面用<include>指令包含的页面以及<forward>标记包含的其它jsp页面; 

      Session:同一个http会话,关闭浏览器就可以结束了。同一个http会话中的web组件共享它;
      Application:整个web应用的所用web组件共享它。除非关闭tomcat,否则无法关闭。

     单点登录

     解决多web工程间的相互登陆问题。 即session共享问题

    一、什么是单点登录SSO(Single Sign-On)

      SSO是一种统一认证和授权机制,指访问同一服务器不同应用中的受保护资源的同一用户,只需要登录一次,即通过一个应用中的安全验证后,再访问其他应用中的受保护资源时,不再需要重新登录验证。

    二、单点登录解决了什么问题

      解决了用户只需要登录一次就可以访问所有相互信任的应用系统,而不用重复登录。

    集群环境下会出现要求用户多次登录的情况。

    解决方案:

    可以使用Session服务器,保存Session信息,使每个节点是无状态。需要模拟Session。

    单点登录系统是使用redis模拟Session,实现Session的统一管理。

     具体实施

            <form id="regForm_mod" name="regForm_mod" method="post"  >
    
                <li class="regMb30">
                    <label><font>*</font> 账户名:</label>
                <span class="regM defaultBorder">
                    <input id="regName" name="username"  class="regInput" type="text" onkeyup="mail_div(event);" onfocus="showtip('regName','userMamErr',8);" onblur="ckmem();" autocomplete="off" maxlength="80"/>
                    <em></em>
                </span>
                    <span class="regInput" id="userMamErr"></span>
                </li>
                <div node-type="layer" class="accountSearch" style="display:none;" id="person_mail"></div>
                <li>
                    <label><font>*</font> 登录密码:</label>
                <span class="regM defaultBorder">
                    <input id="pwd" name="password" class="regInput" autocomplete="off" type="password" onfocus="showPwdtip('password','passwordErr',2);"  onkeyup="ckpwd(0,1);" onblur="ckpwd(0,0);"/>
                    <em ></em>
                </span>
                    <span class="regInput" id="passwordErr"></span>
                </li>
                <li class="safetyLayer regPl191" id="pwdStrong">
                    <font style="font-size:12px;">安全程度:</font><em class="default"></em><em class="default"></em><em class="default"></em>
                </li>
    
                <li class="regMb30">
                    <label><font>*</font> 确认密码:</label>
                <span class="regM defaultBorder">
                    <input id="pwdRepeat" name="password2" autocomplete="off" class="regInput" type="password" onfocus="showtip('password2','password2Err',3);" onblur="ckpwd2();"/>
                    <em></em>
                </span>
                    <span class="regInput" id="password2Err"></span>
                </li>
                <li class="regMb30">
                    <label><font>*</font> 验证手机:</label>
                <span class="regM defaultBorder">
                    <input id="phone" name="phone" autocomplete="off" class="regInput" type="text" maxlength="11" onfocus="showtip('phone','phoneErr',1);" onblur="$('#phoneErr').html('')"/>
                    <em></em>
                </span>
                    <span class="regInput" id="phoneErr"></span>
                </li>
    
                
    
                <li class="regPl88">
                <span  class="regM" style="margin-left:29px">
                    <input id="AgreeId" name="AgreeId" type="checkbox" checked="" onclick="ckAgree();">
                    <a href="https://passport.e3mall.cn/xy.html" target="_blank"  class="checkTitle">我已阅读并同意<font style="font-size:12px;">《宜立方商城用户注册协议》</font></a>
                </span>
                    <span  id="AgreeIdErr" ></span>
                </li>
                <li class="register regPl88 regMt10" id="sub_per" style="margin-left:29px">
                    <input type="hidden" id="tjuid" name="tjuid" value="">
                    <a href="javascript:void(0);" class="registerNow" id="reg_per_data" onclick="REGISTER.reg()">立即注册</a>
                </li>
                <br /><br />
            </form>

     1.触发注册

    onclick="REGISTER.reg()"
    2,script采用json变量存储代码
    <script type="text/javascript">
        var REGISTER={
            param:{
                //单点登录系统的url
                surl:""
            },
            inputcheck:function(){
                var flag = true;
                //不能为空检查
                if ($("#regName").val() == "") {
                    showError("regName","userMamErr",defaultArr[8]);
                    flag = false;
                }
                if ($("#pwd").val() == "") {
                    showError("pwd","passwordErr",pwdArr[0]);
                    flag = false;
                }
                if ($("#phone").val() == "") {
                    showError("phone","phoneErr",memArr[0]);
                    flag = false;
                }
                //密码检查
                if ($("#pwd").val() != $("#pwdRepeat").val()) {
                    showError("pwdRepeat","password2Err",pwd2Arr[1]);
                    flag = false;
                }
                return flag;
            },
            beforeSubmit:function() {
                    //检查用户是否已经被占用
                    //escape() 函数可对字符串进行编码,这样就可以在所有的计算机上读取该字符串。
                    //采用Math.random()为了防止浏览器默认为相同而缓存,包证每次url都不一样
                    $.ajax({
                        url : REGISTER.param.surl + "/user/check/"+escape($("#regName").val())+"/1?r=" + Math.random(),
                        success : function(data) {
                            if (data.data) {
                                //检查手机号是否存在
                                $.ajax({
                                    url : REGISTER.param.surl + "/user/check/"+$("#phone").val()+"/2?r=" + Math.random(),
                                    success : function(data) {
                                        if (data.data) {
                                            REGISTER.doSubmit();
                                        } else {
                                            showError("phone","phoneErr",defaultArr[9]);
                                        }
                                    }
                                });
                            } else {
                                showError("regName","userMamErr",defaultArr[10]);
                            }    
                        }
                    });
                        
            },
            doSubmit:function() {
                $.post("/user/register",$("#regForm_mod").serialize(), function(data){
                    if(data.status == 200){
                        jAlert('用户注册成功,请登录!',"提示", function(){
                            REGISTER.login();
                        });
                    } else {
                        jAlert("注册失败!","提示");
                    }
                });
            },
            login:function() {
                 location.href = "/page/login";
                 return false;
            },
            reg:function() {
                if (this.inputcheck()) {
                    this.beforeSubmit();
                }
            }
        };
    </script>
     3.
    REGISTER.reg();
    3.1 先验证输入完整性;
    this.inputcheck()
    inputcheck:function(){
                var flag = true;
                //不能为空检查
                if ($("#regName").val() == "") {
                    showError("regName","userMamErr",defaultArr[8]);
                    flag = false;
                }
                if ($("#pwd").val() == "") {
                    showError("pwd","passwordErr",pwdArr[0]);
                    flag = false;
                }
                if ($("#phone").val() == "") {
                    showError("phone","phoneErr",memArr[0]);
                    flag = false;
                }
                //密码检查
                if ($("#pwd").val() != $("#pwdRepeat").val()) {
                    showError("pwdRepeat","password2Err",pwd2Arr[1]);
                    flag = false;
                }
                return flag;
            }


    3.2检查用户是否已经被占用(ajax)
           beforeSubmit:function() {
                    //检查用户是否已经被占用
                    //escape() 函数可对字符串进行编码,这样就可以在所有的计算机上读取该字符串。
                    //采用Math.random()为了防止浏览器默认为相同而缓存,包证每次url都不一样
                    $.ajax({
                        url : REGISTER.param.surl + "/user/check/"+escape($("#regName").val())+"/1?r=" + Math.random(),
                        success : function(data) {
                            if (data.data) {
                                //检查手机号是否存在
                                $.ajax({
                                    url : REGISTER.param.surl + "/user/check/"+$("#phone").val()+"/2?r=" + Math.random(),
                                    success : function(data) {
                                        if (data.data) {
                                            REGISTER.doSubmit();
                                        } else {
                                            showError("phone","phoneErr",defaultArr[9]);
                                        }
                                    }
                                });
                            } else {
                                showError("regName","userMamErr",defaultArr[10]);
                            }    
                        }
                    });
    controller
        @RequestMapping("/user/check/{param}/{type}")
        @ResponseBody
        public E3Result checkData(@PathVariable String param, @PathVariable Integer type) {
            E3Result e3Result = userService.checkData(param, type);
            return e3Result;
        }
    public E3Result checkData(String param, Integer type) {
            // 1、从tb_user表中查询数据
                    UserExample example = new UserExample();
                    Criteria criteria = example.createCriteria();
                    // 2、查询条件根据参数动态生成。
                    //1、2、3分别代表username、phone、email
                    if (type == 1) {
                        criteria.andUsernameEqualTo(param);
                    } else if (type == 2) {
                        criteria.andPhoneEqualTo(param);
                    } else if (type == 3) {
                        criteria.andEmailEqualTo(param);
                    } else {
                        return E3Result.build(400, "非法的参数");
                    }
                    //执行查询
                    List<User> list = userMapper.selectByExample(example);
                    // 3、判断查询结果,如果查询到数据返回false。
                    if (list == null || list.size() == 0) {
                        // 4、如果没有返回true。
                        return E3Result.ok(true);
                    } 
                    // 5、使用e3Result包装,并返回。
                    return E3Result.ok(false);
                }

    3.3 提交验证
     REGISTER.doSubmit();
    doSubmit:function() {
                $.post("/user/register",$("#regForm_mod").serialize(), function(data){
                    if(data.status == 200){
                        jAlert('用户注册成功,请登录!',"提示", function(){
                            REGISTER.login();
                        });
                    } else {
                        jAlert("注册失败!","提示");
                    }
                });
            }


    /**
         * 注册
         * @return
         */
        @RequestMapping(value="/user/register",method=RequestMethod.POST)
        @ResponseBody
        public E3Result register(User user){
            E3Result result = userService.createUser(user);
            return result;        
        }
    public E3Result createUser(User user) {
            // 1、使用TbUser接收提交的请求。
            
            if (StringUtils.isBlank(user.getUsername())) {
                return E3Result.build(400, "用户名不能为空");
            }
            if (StringUtils.isBlank(user.getPassword())) {
                return E3Result.build(400, "密码不能为空");
            }
            //校验数据是否可用
            E3Result result = checkData(user.getUsername(), 1);
            if (!(boolean) result.getData()) {
                return E3Result.build(400, "此用户名已经被使用");
            }
            //校验电话是否可以
            if (StringUtils.isNotBlank(user.getPhone())) {
                result = checkData(user.getPhone(), 2);
                if (!(boolean) result.getData()) {
                    return E3Result.build(400, "此手机号已经被使用");
                }
            }
            //校验email是否可用
            if (StringUtils.isNotBlank(user.getEmail())) {
                result = checkData(user.getEmail(), 3);
                if (!(boolean) result.getData()) {
                    return E3Result.build(400, "此邮件地址已经被使用");
                }
            }
            // 2、补全TbUser其他属性。
            user.setCreated(new Date());
            user.setUpdated(new Date());
            // 3、密码要进行MD5加密。
            String md5Pass = DigestUtils.md5DigestAsHex(user.getPassword().getBytes());
            user.setPassword(md5Pass);
            // 4、把用户信息插入到数据库中。
            userMapper.insert(user);
            // 5、返回e3Result
            return E3Result.ok();
        }
    密码进行md5加密:import org.springframework.util.DigestUtils;
     String md5Pass = DigestUtils.md5DigestAsHex(user.getPassword().getBytes());
    登陆功能
    1.进行验证
    获取用户名和密码
       @RequestMapping(value="/user/login",method=RequestMethod.POST)
        @ResponseBody
        public E3Result login(String username, String password,
                HttpServletRequest request, HttpServletResponse response){
            // 1、接收两个参数。
            // 2、调用Service进行登录。
            E3Result result = userService.login(username, password);
            //判断是否登录成功
            if(result.getStatus()==200){
                // 3、从返回结果中取token,写入cookie。Cookie要跨域。
                String token = result.getData().toString();
                CookieUtils.setCookie(request, response,TOKEN_KEY, token);
            }        
            // 4、响应数据。Json数据。e3Result,其中包含Token。
            return result;
        }
    2. E3Result result = userService.login(username, password);
    验证成功后,生成模拟的session存储于redis

    public E3Result login(String username, String password) {
            // 1、判断用户名密码是否正确。
            UserExample example = new UserExample();
            Criteria criteria = example.createCriteria();
            criteria.andUsernameEqualTo(username);
            //查询用户信息
            List<User> list = userMapper.selectByExample(example);
            if (list == null || list.size() == 0) {
                return E3Result.build(400, "用户名或密码错误");
            }
            User user = list.get(0);
            //校验密码
            if (!user.getPassword().equals(DigestUtils.md5DigestAsHex(password.getBytes()))) {
                return E3Result.build(400, "用户名或密码错误");
            }
            // 2、登录成功后生成token。Token相当于原来的jsessionid,字符串,可以使用uuid。
            String token = UUID.randomUUID().toString();
            // 3、把用户信息保存到redis。Key就是token,value就是TbUser对象转换成json。
            // 4、使用String类型保存Session信息。可以使用“前缀:token”为key
            user.setPassword(null);
            jedisClient.set("SESSION:" + token, JsonUtils.objectToJson(user));
            // 5、设置key的过期时间。模拟Session的过期时间。一般半个小时。
            jedisClient.expire( "SESSION:" + token, SESSION_EXPIRE);
            // 6、返回e3Result包装token。
            return E3Result.ok(token);
        }
     user.setPassword(null);//为了密码的安全性
    3.将模拟session存储于cookie
        //判断是否登录成功
            if(result.getStatus()==200){
                // 3、从返回结果中取token,写入cookie。Cookie要跨域。
                String token = result.getData().toString();
                CookieUtils.setCookie(request, response,TOKEN_KEY, token);
            }  
    redis上的key值为了唯一性,key=
    "SESSION:" +UUID.randomUUID().toString()


    所以不用用户id

    为什么要存储到cookie?
    cookie存储到客户端,相当于开启免登录。如果存放到session是存放到服务器,每次都去取占用服务器资源。而且这里已经用redis模仿了session。

    cookie 和session 的区别:

    1、cookie数据存放在客户的浏览器上,session数据放在服务器上。

    2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗
       考虑到安全应当使用session。

    3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能
       考虑到减轻服务器性能方面,应当使用COOKIE。

    4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

    5、所以个人建议:
       将登陆信息等重要信息存放为SESSION
       其他信息如果需要保留,可以放在COOKIE中

    • 这里使用了自己写的CookieUtils
     CookieUtils.setCookie(request, response,TOKEN_KEY, token);//不设置不设置生效时间默认浏览器关闭即失效,也不编码

    /**
    * 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码
    */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
    String cookieValue) {
        setCookie(request, response, cookieName, cookieValue, -1);
    }

      //这里isEncode=false

    /**
    * 设置Cookie的值 在指定时间内生效, 编码参数
    */
    public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
    String cookieValue, int cookieMaxage, boolean isEncode) {
      doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);
    }

     /**
         * 设置Cookie的值,并使其在指定时间内生效
         * 
         * @param cookieMaxage cookie生效的最大秒数
         */
        private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
                String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
            try {
                if (cookieValue == null) {
                    cookieValue = "";
                } else if (isEncode) {
                    cookieValue = URLEncoder.encode(cookieValue, "utf-8");
                }
                Cookie cookie = new Cookie(cookieName, cookieValue);//设置cookie值
                if (cookieMaxage > 0)
                    cookie.setMaxAge(cookieMaxage);//设置cookie生存时间
                if (null != request) {// 设置域名的cookie
                    String domainName = getDomainName(request);
                    System.out.println(domainName);
                    if (!"localhost".equals(domainName)) {
                        cookie.setDomain(domainName);
                    }
                }
                cookie.setPath("/");
                response.addCookie(cookie);
            } catch (Exception e) {
                 e.printStackTrace();
            }
        }

    cookie要做到域名隔离。这个方法是为了提取

    获取域名:(为了指定cookie的作用范围)
    /**
         * 得到cookie的域名
         */
        private static final String getDomainName(HttpServletRequest request) {
            String domainName = null;
    
            String serverName = request.getRequestURL().toString();
            if (serverName == null || serverName.equals("")) {
                domainName = "";
            } else {
                serverName = serverName.toLowerCase();
                serverName = serverName.substring(7);
                final int end = serverName.indexOf("/");
                serverName = serverName.substring(0, end);
                final String[] domains = serverName.split("\.");
                int len = domains.length;
                if (len > 3) {
                    // www.xxx.com.cn
                    domainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
                } else if (len <= 3 && len > 1) {
                    // xxx.com or xxx.cn
                    domainName = "." + domains[len - 2] + "." + domains[len - 1];
                } else {
                    domainName = serverName;
                }
            }
    
            if (domainName != null && domainName.indexOf(":") > 0) {
                String[] ary = domainName.split("\:");
                domainName = ary[0];
            }
            return domainName;
        }
    这里默认采用localhost作为域名设置,在不同工程间传递,cookie任然有效
     if (null != request) {// 设置域名的cookie
                    String domainName = getDomainName(request);
                    System.out.println(domainName);
                    if (!"localhost".equals(domainName)) {
                        cookie.setDomain(domainName);//
                    }
            cookie.setPath("/");
                response.addCookie(cookie);//cookie the Cookie to return to the client返回的客户端的路径
     

    首页展示用户名

    1、当用户登录成功后,在cookie中有token信息。

    2、从cookie中取token根据token查询用户信息。

    3、把用户名展示到首页。


    根据token获取用户信息
    cookie因为是在客户端,各个工程间是公用的,从cookie中得到token后去查询redis的用户数据。有两种方案:
    1)在Controller中取cookie中的token数据,调用sso服务查询用户信息。(每个controller都要修改麻烦)
    2)当页面加载完成后使用js取token的数据,使用ajax请求查询用户信息。
    所以采用方案2,但面临js跨域读取数据的问题。

    问题:服务接口在sso系统中。Sso.e3.com(localhost:8088),在首页显示用户名称,首页的域名是www.e3.com(localhost:8082),使用ajax请求跨域了。

    Js不可以跨域请求数据。

    Js不可以跨域请求数据。

    什么是跨域:

    1、域名不同

    2、域名相同端口不同。

    解决js的跨域问题可以使用jsonp。

    Jsonp不是新技术,跨域的解决方案。使用js的特性绕过跨域请求。Js可以跨域加载js文件。

    2.1. Json实现

    2.1.1.    客户端

    使用jQuery。

    2.1.2.    服务端

    1、接收callback参数,取回调的js的方法名。

    2、业务逻辑处理。

    3、响应结果,拼接一个js语句。

    跨域传输,其它不变,增加dataType:"jsonp"  就可以实现跨域传送数据了

    下例中的是到另外的web工程获取数据(端口不同)。

    $.ajax({
                url : "http://localhost:8089/user/token/" + _ticket,
                dataType : "jsonp",
                type : "GET",
                success : function(data){
                    if(data.status == 200){
                        var username = data.data.username;
                        var html = username + ",欢迎来到宜立方购物网!<a href="http://www.e3mall.cn/user/logout.html" class="link-logout">[退出]</a>";
                        $("#loginbar").html(html);
                    }
                }
            })
    在跨域时,请求数据,默认会带参数callback
    @RequestMapping("/user/token/{token}")
        @ResponseBody
        public Object getUserByToken(@PathVariable("token") String token,String callback){        
            E3Result result = userService.getUserByToken(token);
            //响应结果之前,判断是否为jsonp请求
            if(StringUtils.isNotBlank(callback)){
                //把结果封装成一个js语句响应
                MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result);
                mappingJacksonValue.setJsonpFunction(callback);
                return mappingJacksonValue;
            }
            return result;        
        }

    在首页显示用户名查询cookie通过jQuery来实现:
     <script type="text/javascript" src="/js/e3mall.js"></script>

    json变量的js
    var E3MALL = {
        checkLogin : function(){
            var _ticket = $.cookie("token");//读取cookie:
    if(!_ticket){//cokie值为空就返回空
                return ;
            }
         //不为空,就到redis获取 $.ajax({ url :
    "http://localhost:8089/user/token/" + _ticket, dataType : "jsonp", type : "GET", success : function(data){ if(data.status == 200){ var username = data.data.username; var html = username + ",欢迎来到宜立方购物网!<a href="http://www.e3mall.cn/user/logout.html" class="link-logout">[退出]</a>"; $("#loginbar").html(html); } } }); } } $(function(){ // 查看是否已经登录,如果已经登录查询登录信息 E3MALL.checkLogin(); });
    加载页面就检查是否登陆
    $(function(){
        // 查看是否已经登录,如果已经登录查询登录信息
        E3MALL.checkLogin();
    });
    登陆就通过redis获取数据重新设置redis模拟session数据存储时间后存储到cookie
    @Override
        public E3Result getUserByToken(String token) {
            //从redis缓存中获取后存储到cookie中
            String json = jedisClient.get("SESSION:"+token);
            if(StringUtils.isBlank(json)){
                // 3、如果查询不到数据。返回用户已经过期。
                return E3Result.build(400, "用户登录已经过期,请重新登陆");
            }
            // 4、如果查询到数据,说明用户已经登录        
            // 5、需要重置key的过期时间
            jedisClient.expire("SESSION:"+token, SESSION_EXPIRE);
            //把json字符串转化为pojo
            User user = JsonUtils.jsonToPojo(json, User.class);
            return E3Result.ok(user);
        }
    返回数据包装为js语句返回:(创建callback函数)
     //把结果封装成一个js语句响应
                MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result);
                mappingJacksonValue.setJsonpFunction(callback);
                return mappingJacksonValue;
    成功后对数据进行处理:(将首页的请登录html进行替换)
    $.ajax({
                url : "http://localhost:8089/user/token/" + _ticket,
                dataType : "jsonp",
                type : "GET",
                success : function(data){
                    if(data.status == 200){
                        var username = data.data.username;
                        var html = username + ",欢迎来到宜立方购物网!<a href="http://www.e3mall.cn/user/logout.html" class="link-logout">[退出]</a>";
                        $("#loginbar").html(html);
                    }
                }
            });
    var html = username + ",欢迎来到宜立方购物网!<a href="http://www.e3mall.cn/user/logout.html" class="link-logout">[退出]</a>";
     $("#loginbar").html(html);
    替换掉以下的html
    <span id="loginbar" style="margin-right: 15px;">
                  <a href="http://localhost:8089/page/login">请登录</a>
              </span>
    就这样实现了首页显示用户信息,同时也实现了人为刷新后,重新设置redis用户时间。







  • 相关阅读:
    Java 知识点(转)
    List集合的clear方法
    WPF中控件TextBlock使用(简单)
    数据结构(严蔚敏、吴伟民)——读书笔记-2、 线性表及其基本运算、顺序存储结构
    微信公众号智能绑定功能实现(2014年10月24日 更新)
    多线程操作数据拷贝要加线程锁
    Oracle 静态监听注冊具体解释
    windows下solr7.9+tomcat7环境搭建
    &quot;Hello World &quot; —— 深入理解程序从编译到执行
    zoj 3822 Domination 概率dp 2014牡丹江站D题
  • 原文地址:https://www.cnblogs.com/limingxian537423/p/7664428.html
Copyright © 2011-2022 走看看