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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM