zoukankan      html  css  js  c++  java
  • redis缓存和cookie实现Session共享

     分布式项目中要实现单点登录(SSO - Single Sign On):对于同一个客户端(例如 Chrome 浏览器),只要登录了一个子站(例如 a.com),则所有子站(b.com、c.com)都认为已经登录。 
    比如用户在登录淘宝后,跳转到天猫时就已经登录了。

    用例步骤

    未登录用户访问子站 a.com 进行登录,自动跳转到账户中心的统一登录页 account.com/login
    用户在统一登录页进行登录,登录成功后显示登录跳转页
    显示登录跳转页后自动跳转回 a.com,单点登录完成
    用户在访问 b.com 时无需再次登录

    通过redis缓存和cookie实现单点登录

    整体实现思路如下图所示。

    这里写图片描述

    Session的缓存机制

    1.业务系统发起登录请求,调用SSO用户登录接口;

    2.SSO登录接口的处理逻辑:

    2.1 根据用户名和密码去数据库验证用户是否合法。
    2.2 用户验证通过之后,生成SessionID,并返回给业务系统。
        同时以SessionID为key,存储用户信息到redis缓存中

    SSO登录接口代码:

    public JSONObject userLogin(@RequestBody JSONObject jsonObject){  
              UserLoginResponse userLoginResponss = new UserLoginResponse();  
              try {  
                  logger.info("处理用户登录业务逻辑,接收报文"+jsonObject);  
                  String msgWithDigesta=SecurityUtil.scfMatch(jsonObject.toString(), newXService.getPrivateKey());  
                  //生成实体  
                  User user = JSONObject.parseObject(msgWithDigesta,User.class);  
                  //是否验证用户的密码  
                  boolean isChechPassword = true;  
                  User userInfo = anaService.loginCheckUserInfo(user,isChechPassword);  
                  // 存储用户信息到redis缓存中  
                  String ticket = anaService.storeUserLoginSessionInRedis(userInfo,user.getModuleCode());  
                  userLoginResponss.setRetCode(RetCode.LOGIN_SUCCESS.getCode());  
                  userLoginResponss.setRetMessage("用户登录成功");  
                  userLoginResponss.setTicket(ticket);  
                  userLoginResponss.setStatus(userInfo.getStatus());  
                  userLoginResponss.setIsModifyPassword(userInfo.getIsModifyPassword());  
              } catch (Exception e) {  
                  userLoginResponss.setRetCode(RetCode.LOGIN_FAILED.getCode());  
                  userLoginResponss.setRetMessage(e.getMessage());  
                  logger.info("插入用户数据到表中失败,原因:"+e.getMessage());  
              }  
              logger.info("返回处理用户登录业务逻辑结果,Result{[]}"+JSONObject.toJSONString(userLoginResponss));  
              return JSON.parseObject(JSONObject.toJSONString(userLoginResponss));  
          }  

    存储用户信息到redis缓存的代码:

       /** 
           * 存储用户登录session信息到redis中 
           * @param userInfo 
           * @return 
           */  
          public String storeUserLoginSessionInRedis(User userInfo,String moduleCode) {  
              // 存储用户ticket信息  
              // 使用AES加密登录用户ID生成SessionID,加密密码是配置文件里定义的64位字符串  
              String sessionId = AesUtil.encrypt(String.valueOf(userInfo.getUserId()), newXService.getBizkey());  
              String unique_ticket = userInfo.getSystemId()+sessionId+"_USER_LOGIN";  
              //  
              String ticket = userInfo.getSystemId()+sessionId+System.currentTimeMillis()+"_USER_LOGIN";  
    
              UserLoginSession userLoginSession = new UserLoginSession();  
              userLoginSession.setUserId(String.valueOf(userInfo.getUserId()));  
              userLoginSession.setUserName(userInfo.getUserName());  
              userLoginSession.setUserLoginName(userInfo.getUserLoginName());  
              // 获取权限  
              List<Permission> permissions = getUserPermissions(userInfo.getUserId());  
              userLoginSession.setPermissions(permissions);  
    
             userLoginSession.setModuleCode(StringUtils.killNull(userInfo.getModuleCode()));  
              userLoginSession.setLastLoginTime(userInfo.getLastLoginTime());  
              userLoginSession.seteId(StringUtils.killNull(userInfo.geteId()));  
              userLoginSession.setSessionId(ticket);  
              userLoginSession.setUserInfo(userInfo);      
    
              //限制唯一登录,删除上一个用户信息  
              if (redisService.exists(unique_ticket))  
                  redisService.del(redisService.get(unique_ticket));  
    
              redisService.set(unique_ticket, ticket);  
    
              logger.info("访问AnaController的login方法:放进redis"+ticket);  
              redisService.setKeyExpire((ticket).getBytes(),1800);  
    
              logger.info("userloginsession result ="+JSONObject.toJSONString(userLoginSession));  
              return ticket;  
          }  

    3.业务系统将返回的sessionID,存放到cookie里

    业务系统controller的代码:

         @RequestMapping("/login.ajax")  
         @ResponseBody  
         public  Map<String, Object> login(@RequestParam("username2") String username2,  
                                      @RequestParam("moduleCode2") String moduleCode2,  
                                      @RequestParam("password2") String password2, String requestUrl, HttpServletResponse response) {
                    // 其他业务逻辑省略  
                    String sessionId = userBySso.getTicket();  
                    Cookie cookie = new Cookie("CORE_SESSION", sessionId);  
                    cookie.setPath("/");  
                    response.addCookie(cookie);  
                    // 其他业务逻辑省略 
        }  

    4.业务系统取得Session信息,并检验用户信息

    业务系统的页面发起web请求时,在自定义拦截器(继承自HandlerInterceptor)的preHandle方法里取得session信息,并检查用户是否登录。
    Session信息取得时,首先从cookie中取得SessionId,然后根据SessionId从redis取得用户信息

    自定义拦截器的代码此处省略,请参照【SpringMVC学习笔记2_拦截器实现登录验证】,以下是取得session信息的代码

        public UserLoginSession getUserLoginSession(HttpServletRequest req) {  
              logger.info("访问getUserLoginSession");  
    
              String sessionId = "";  
              Cookie[] cookie = req.getCookies();  
              if (cookie == null) {  
                  return null;  
              }  
              for (int i = 0; i < cookie.length; i++) {  
                  Cookie cook = cookie[i];  
                  if (cook.getName().equals("CORE_SESSION")) {  
                      sessionId = cook.getValue().toString();  
                  }  
              }  
    
              logger.info("访问getUserLoginSession获取sessionId: " + sessionId);  
    
              if ("".equals(sessionId)) {  
    
                  return null;  
              }  
    
              String UserLoginSessionStr = redisService.get(sessionId);  
    
              logger.info("访问getUserLoginSession获取USERLOGINSESSION: " + UserLoginSessionStr);  
    
              if (null == UserLoginSessionStr || "".equals(UserLoginSessionStr)) {  
    
                  return null;  
              }  
    
              UserLoginSession userLoginSession = (UserLoginSession) JSONObject.toJavaObject(JSONObject.parseObject(UserLoginSessionStr), UserLoginSession.class);  
              logger.info("访问getUserLoginSession获取USER_ID成功: " + userLoginSession.getUserId());  
              redisService.setKeyExpire((sessionId).getBytes(), 1800);  
              redisService.setKeyExpire((userLoginSession.getTicketRole()).getBytes(),1800);  
              return userLoginSession;  
          }  

    自定义拦截器的代码此处省略,请参照【SpringMVC学习笔记2_拦截器实现登录验证】,以下是取得session信息的代码
    ---------------------
    作者:hchhan
    来源:CSDN
    原文:https://blog.csdn.net/hchhan/article/details/78390280
    版权声明:本文为博主原创文章,转载请附上博文链接!

    参考:

          https://blog.csdn.net/hchhan/article/details/78390280

  • 相关阅读:
    mysql5.7.22安装步骤
    idea 配置http代理
    大话设计模式之类与类之间的关系读后感
    大话设计模式之工厂方法模式读后感
    rabbitmq+java入门(五)Topic
    rabbitmq+java入门(四)routing
    rabbitmq+java入门(二) 工作队列
    rabbitmq+java入门(三)exchange的使用
    rabbitmq+java入门(一)hello world
    idea+jrebel+springboot热部署
  • 原文地址:https://www.cnblogs.com/xiaohuizhenyoucai/p/11051826.html
Copyright © 2011-2022 走看看