1.需求分析
- jeecgboot 框架要实现同一个账号只允许一个人登录,就跟游戏账号类似,“我登录了就把你踢下去,你登录了就把我踢下去”;
- jwt 原理是生成 token 后一段时间内登录都有效,jeecgboot 中 jwt 和 redis 联合使用后,在 redis 中 用户过期的 key 是
CommonConstant.PREFIX_USER_TOKEN + token,token 几乎是唯一的
, - 想到
JwtUtil.verify(cacheToken, userName, passWord)
方法验证的时候想办法给他返回 false,但是发现只要 token 没过期第一个人登录的还是能访问 - 第二个人登录后,怎样让第一个人访问不了呢?既不能改第一个人浏览器中的 token 信息,又不能把 jwt
verify()
方法重写了(水平不够,放弃重写);那我就直接把第一个人登录的key(CommonConstant.PREFIX_USER_TOKEN+token)
删了吧,第一人登录的 key 都没有了,这样第一人就下线了。 - 怎样获取第一人的 key 呢,改写 key 前缀,原来是
CommonConstant.PREFIX_USER_TOKEN + token
,改成CommonConstant.PREFIX_USER_TOKEN + admin + ip
,去 redis 里面查询前缀是CommonConstant.PREFIX_USER_TOKEN + admin
的 samePrefixKeys ,然后把 samePrefixKeys 都删了
2.上代码
private void onlyOnePlaceLogin(String username,String id) {List<String> keys = redisUtil.keys(CommonConstant.PREFIX_USER_TOKEN+username);keys.forEach(key -> deleteLoginInfo(key,username,id));}
//参考 logout 方法
private void deleteLoginInfo(String key, String username,String id) {//清空用户登录Token缓存redisUtil.del(CommonConstant.PREFIX_USER_TOKEN + username);//清空用户登录Shiro权限缓存redisUtil.del(CommonConstant.PREFIX_USER_SHIRO_CACHE + id);//清空用户的缓存信息(包括部门信息),例如sys:cache:user::<username>redisUtil.del(String.format("%s::%s", CacheConstant.SYS_USERS_CACHE, username));}
3.关联修改
在项目中所有用到 CommonConstant.PREFIX_USER_TOKEN + token
key 的地方换成CommonConstant.PREFIX_USER_TOKEN + name + ip
4.最后
结束了,这个方案还没真正上线,只是本地测试没问题,欢迎大家讨论
5.参考
jeecgboot 登录用户唯一实现方式
Springboot集成JWT+Redis实现单点登录和同一账号只允许在一处登录