在web應用中假如沒有做會話控制,會出現這樣的情況,A登錄了賬號,B也登錄了賬號,都是同樣的賬號,A修改了信息,B會看到修改的信息,這樣的用戶體驗不好,B會覺得我沒有修改啊,為什么信息會改變。而做會話控制后,A先登錄,B再登錄,那么B會把A的登錄擠下線。
實現思路:采用時間戳比較
1首先用戶登錄時,后台是不需要做攔截的,前台把用戶名和密碼傳到后台,后台生成JWT格式的token給前台,並以token為key,用戶信息為value存入redis中
2其他url路徑,過濾器會攔截請求,先判斷前台是否攜帶了token,或者是攜帶了token,但是已經失效了(redis中查不到)會直接返回前台錯誤信息,這時候會以用戶名為key,從redis中查詢token,沒有就以key為用戶名,value為token存入redis.放行請求,若從redis查詢的token和從前台傳進來的token相同,放行請求,若從redis查詢的token和從前台傳進來的token不相同,如果從前台傳遞的token的時間戳大於redis中的,則覆蓋redis中的token,否認注銷token,返回您的賬號已經在其他設備登錄,攔截請求。
public class CompareKickOutFilter extends KickOutFilter { @Override public boolean isAccessAllowed(HttpServletRequest request, HttpServletResponse response) { String token = request.getHeader("Authorization"); String username = JWTUtil.getUsername(token); String userKey = PREFIX + username; RBucket<String> bucket = redissonClient.getBucket(userKey); String redisToken = bucket.get(); if (StringUtils.isBlank(redisToken)) {// 第一次設置 bucket.set(token); } else if (token.equals(redisToken)) { // 相同的token return true; } else { Long redisTokenUnixTime = JWTUtil.getClaim(redisToken, "createTime").asLong(); Long tokenUnixTime = JWTUtil.getClaim(token, "createTime").asLong(); if (tokenUnixTime.compareTo(redisTokenUnixTime) > 0) { // 傳進來的token是離現在最新的,覆蓋舊的token bucket.set(token); } else { // 注銷當前token userService.logout(token); sendJsonResponse(response, 4001, "您的賬號已在其他設備登錄"); return false; } } return true; } }
項目結構:
項目地址:https://github.com/jake1263/loginCtl