最近在研究security单设备登陆,单体的security(不使用oauth2)可以用内置的session设置session最大数量来实现单设备。oauth2的jwt该如何来实现单设备登陆?
参考了几篇文章,是继承DefaultTokenServices或者实现TokenService,但是好麻烦,最重要的是,,我没看懂。。。不知道该实现哪个类
但是。。代码还是得实现的,条条道路通罗马,不一定非得重写
先说一下我的项目的oauth流程:
用户user/管理员admin通过/user/login或/admin/login的controller来进入业务层,业务层通过restTemplate调用自身的/login/token,传入用户或管理员的client_id及对应的secret,来分离前后端登陆,上图
到UserDetailsService的实现类时,通过SecurityContextHolder中传入的client_id的值来判断调用哪个feign来查询
当security内部校验通过后,生成jwt后,把jwt的jti作为key,jwt作为value存储到redis,jti给客户
当客户携带着jti访问资源服务器时,先经过gateway进行请求的增强,Authorization为消息头,值时Bearer+空格+jwt(jwt通过jti从redis中取出,redis没用这个值,要么没登陆,要么过期,返回401/302跳转),放行至下游资源服务器
捋了捋流程,单设备的实现方法好像也有思路了
我想了个思路,jtw再创建后,返回给客户之前,先用一个特定的字符串+id(id是雪花算法生成的id,不存在重复的问题)拼成一个key(后面都简称key了),value是jti,以key-value的形式存到redis中。下一次登陆时,先判断这个key再redis中是否存在,(顶号登陆,后登录的,踢下前一台设备)存在则删除key-jti组合和jti-jwt组合,不存在说明没有设备登陆
上图
测试一下:
第一次登陆
postman再次登陆:
redis中依旧只有两条数据,原有的两条数据已经被删除,完美实现顶号登陆!
禁止第二台设备登陆,现在只有思路,但是还未实现
登陆时需要把ip+设备号或其他一些证明身份的信息生成一个md5,来标志一个设备,如果同一账号且同一设备登陆,把第一个登陆删掉就行(可能是用户直接关掉了浏览器而未注销登陆),但是如果同一账号不同设备,则禁止该设备登陆(同一账号多地登陆的情况)
因代码太多,只展示了部分代码截图,项目完成之后会推送到jitee上