最近項目出現新需求,產品經理提出一些用戶要限制同一用戶同時登錄多台設備,一些用戶不需要限制,也可以在多台設備上同時登錄,想了好久沒有太多的思路;后面和同事討論,才想出了使用 redis 緩存當前用戶的登錄狀態,然后根據已登錄用戶的狀態來限制用戶再次登錄,為用戶分配指定角色,根據角色判斷是否限制用戶同時登錄多台設備,下面我就來將具體的步驟羅列出來;
大致介紹:項目是采用 Shiro + JWT + Redis 來記錄當前用戶的登錄狀態,前端登錄時,后端通過校驗用戶名和密碼,通過后生成 JWT 並返回給前端,后續每次請求前端都會攜帶 token 訪問后端,后端校驗 token 的有效性,進而操作指定的功能。
解決方案:用戶登錄時,Redis 再維護以 username【唯一】作為 key,value = token 的鍵值對數據,並設置一定的過期時長;創建一個限制用戶登錄的角色 limit_login ,當獲取已登錄的用戶擁有該角色 limit_login 時,就需要判定用戶是否有同時登錄其他設備。
業務代碼
1)用戶登錄時,JWT 根據用戶名,生成加密 token,並保存在 Redis 緩存中;
// 生成 Token String token = JwtUtil.sign(sysUser.getUsername(), syspassword); // 緩存用戶登錄生成的 token,並設置 key 的過期時間 redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token); redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 1000); // 緩存用戶登錄狀態,並設置 key 的過期時間 redisUtil.set(CommonConstant.PREFIX_USER_LOGIN_STATUS + username, token); redisUtil.expire(CommonConstant.PREFIX_USER_LOGIN_STATUS + username, JwtUtil.EXPIRE_TIME / 1000);
2)限制用戶同時登陸需要設置指定角色 limit_login_role,后續再次登錄時,需要驗證用戶是否擁有該角色?驗證用戶是否限制了同時登陸?
private final String limitLoginRole = "limit_login_role"; // 通過用戶名獲取用戶角色列表 List<String> roleList = sysUserService.getRole(username); if (roleList.contains(limitLoginRole)) { throw new RuntimeException("該用戶已登錄"); }
3)用戶登錄后正常操作系統,需校驗 token 有效性,是否過期,校驗成功后,刷新 token 和 loginStatus 過期時間
// 從 Redis 中獲取 token 數據 String cacheToken = String.valueOf(redisUtil.get(CommonConstant.PREFIX_USER_TOKEN + token)); if (StringUtils.isNotEmpty(cacheToken)) { // 校驗token有效性 if (JwtUtil.verify(token, userName, passWord)) {
// 緩存用戶登錄生成的 token,並設置 key 的過期時間 redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token); redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 1000); // 緩存用戶登錄狀態,並設置 key 的過期時間 redisUtil.set(CommonConstant.PREFIX_USER_LOGIN_STATUS + username, token); redisUtil.expire(CommonConstant.PREFIX_USER_LOGIN_STATUS + username, JwtUtil.EXPIRE_TIME / 1000);
}
}
4)登錄登錄時,刪除 Redis 中的 token 和 loginStatus
String token = request.getHeader(DefContants.X_ACCESS_TOKEN); //清空用戶Token緩存 redisUtil.del(CommonConstant.PREFIX_USER_TOKEN + sysUser.getUsername()); //清空用戶登錄狀態緩存 redisUtil.del(CommonConstant.PREFIX_USER_LOGIN_STATUS + sysUser.getUsername());
以上就是通過 Shiro + JWT + Redis 實現限制用戶同時登錄的核心代碼,不管用戶有沒有分配限制同時登陸的角色,都會保存一個 loginStatus = token 這樣一個鍵值對,如果用戶分配了該角色,就會校驗,如果用戶沒分配該角色,直接走之前的 token 校驗即可,忽略限制用戶同時登錄角色的校驗。