java實現多設備同時登錄或強制下線
前言:你有沒有遇到過這樣的需求,產品要求實現同一個用戶根據后臺設置允許同時登錄,或者不準同時登錄時,需要強制踢下線前一個的場景。本文將帶領大家實現一個簡單的這種場景需求。
先來看一下簡單的時序圖,方便后續(xù)理解。
首先我們需要有一個后臺設置開關來控制允不允許用戶多設備同時登錄的功能(沒有也無妨,假定允許),其次在登錄后,需要保存用戶的userId-token的關系緩存。再回頭看上面的時序圖,是不是已經能理解實現的原理了。
如果你的架構是微服務,那么可以使用redis來存登錄關系緩存,單體架構可以直接存session即可。本文是微服務架構,所以采用的是redis。
本文的前提都是基于同一個用戶的情況下,下文不再贅述。
1 構造登錄緩存關系
如果要實現同一用戶多設備同時登錄,那必然需要在session(微服務中可以用redis做session共享)中能找到用戶的每一個登錄狀態(tài),如果只是簡單的緩存用戶信息是實現不了的,登錄時那就必須要有一個唯一值token,這樣每次登錄token不一樣,但是指向的用戶是同一個。
usertoken
中維護的是前綴:用戶id,這里不需要維護多個,因為用的reids的hash數據類型,多個登錄時,添加新行即可;user
部分,這里維護的是多個,即登錄一次就有一條記錄;因為根據業(yè)務需要,后續(xù)需要從緩存中獲取用戶其他信息。
- 允許多設備同時登錄:
usertoken
只有1條,user
可能會有多條 - 不允許多設備同時登錄(有則強制下線):
usertoken
只有1條,user
只有1條
/** * 登錄成功后緩存用戶信息 * * @param req * @return */ public void cacheUserInfo(CacheUserInfoReqDTO req) { // 1、緩存用戶信息 cacheUser(req); cacheAuth(req.getUid(), req.getRoles(), req.getPermissions()); // 2、更新token與userId關系 String userTokenRelationKey = RedisKeyHelper.getUserTokenRelationKey(req.getEntId() + SymbolConstant.COLON + req.getUid()); redisAdapter.set(userTokenRelationKey, req.getToken(), RedisTtl.USER_LOGIN_SUCCESS); }
2 過濾器配置
登錄鑒權部分和用戶登錄狀態(tài)上下文不在本文范圍內,此處忽略
登錄成功后,每一個請求到達過濾器時,通過請求header中的token
來獲取登錄信息;因為我們存的緩存key前綴都包含userId,所以要想得到用戶信息,需要使用到redis的scan命令來獲取。(scan最好配置count來限制,保證性能)
@Override protected Mono<Void> innerFilter(ServerWebExchange exchange, WebFilterChain chain) { String token = filterContext.getToken(); if (StringUtils.isBlank(token)) { throw new DataValidateException(GatewayReturnCodes.TOKEN_MISSING); } // scan獲取user的key String userKey = ""; Set<String> scan = redisAdapter.scan(GatewayRedisKeyPrefix.USER_KEY.getKey() + "*" + token); if (scan.isEmpty()) { throw new DataValidateException(GatewayReturnCodes.TOKEN_EXPIRED_LOGIN_SUCCESS); } userKey = scan.iterator().next(); MyUser myUser = (MyUser) redisAdapter.get(userKey); if (myUser == null) { throw new BusinessException(GatewayReturnCodes.TOKEN_EXPIRED_LOGIN_SUCCESS); } // 將用戶信息塞入http header // do something... return chain.filter(exchange.mutate().request(newServerHttpRequest).build()); }
這樣保證即使有多設備同時登錄,也能獲取到登錄信息和上下文。
3 如何做強制下線呢?
其實也很簡單,在登錄前可以通過AOP
方式做校驗,如果已登錄了,那么這里就清除session或用戶緩存,再繼續(xù)進行正常登錄即可。再簡單一點可以直接在登錄service中添加校驗
核心邏輯
String userTokenRelationKey = RedisKeyHelper.getUserTokenRelationKey(req.getEntId() + SymbolConstant.COLON + userEntList.get(0).getUserId()); String redisToken = (String) redisAdapter.get(userTokenRelationKey); if (StringUtils.isNotEmpty(redisToken) && !redisToken.equals(token)) { throw new BusinessException(UserReturnCodes.MULTI_DEVICE_LOGIN); }
這里用于判斷是否已有登錄,并返回給前端提示。用于前端其他業(yè)務處理如果不需要給前端提示,不用返回前端,直接進行清除session或用戶緩存邏輯。
String userTokenRelationKey = RedisKeyHelper.getUserTokenRelationKey(req.getEntId() + SymbolConstant.COLON + userEntity.getId()); // 獲取當前已用戶的登錄token String redisToken = (String) redisAdapter.get(userTokenRelationKey); // 踢下線之前全部登錄 Response<Void> exitLoginResponse = gatewayRpc.allExit(ExitLoginReqDTO.builder().token(redisToken).userId(userEntity.getId()).build());
4 演示
演示強制下線
這里我用用戶id為4做演示
先正常第一次登錄,提示成功,并且redis中有1條user
記錄
再次登錄,我這里是返回給前端處理了,所以會有提示信息。
前端效果
最后,擴展一下,如果要實現登錄后強制修改默認密碼、登錄時間段限制等場景,你會怎么實現呢?
到此這篇關于java實現多設備同時登錄或強制下線的文章就介紹到這了,更多相關java 多設備同時登錄或強制下線內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
在Mac OS上安裝Java以及配置環(huán)境變量的基本方法
這篇文章主要介紹了在Mac OS上安裝Java以及配置環(huán)境變量的基本方法,包括查看所安裝Java版本的方法,需要的朋友可以參考下2015-10-10springBoot Junit測試用例出現@Autowired不生效的解決
這篇文章主要介紹了springBoot Junit測試用例出現@Autowired不生效的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09