當前位置:
首頁 > 知識 > redis緩存和cookie實現Session共享

redis緩存和cookie實現Session共享

分散式項目中要實現單點登錄(SSO - Single Sign On):對於同一個客戶端(例如 Chrome 瀏覽器),只要登錄了一個子站(例如 a.com),則所有子站(b.com、c.com)都認為已經登錄。

比如用戶在登錄淘寶後,跳轉到天貓時就已經登錄了。

用例步驟

未登錄用戶訪問子站 a.com 進行登錄,自動跳轉到賬戶中心的統一登錄頁 account.com/login

用戶在統一登錄頁進行登錄,登錄成功後顯示登錄跳轉頁

顯示登錄跳轉頁後自動跳轉回 a.com,單點登錄完成

用戶在訪問 b.com 時無需再次登錄

  • 1
  • 2
  • 3
  • 4
  • 5

通過redis緩存和cookie實現單點登錄

整體實現思路如下圖所示。

redis緩存和cookie實現Session共享

Session的緩存機制

1.業務系統發起登錄請求,調用SSO用戶登錄介面;

2.SSO登錄介面的處理邏輯:

2.1 根據用戶名和密碼去資料庫驗證用戶是否合法。

2.2 用戶驗證通過之後,生成SessionID,並返回給業務系統。

同時以SessionID為key,存儲用戶信息到redis緩存中

  • 1
  • 2
  • 3

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));

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

存儲用戶信息到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;

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

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);

// 其他業務邏輯省略

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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;

}

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序員小新人學習 的精彩文章:

我是如何從煤礦工成為程序員的
TensorFlow中的所有模型

TAG:程序員小新人學習 |