Sa-Token登录pre
个人博客:无奈何杨(wnhyang)
个人语雀:wnhyang
共享语雀:在线知识共享
Github:wnhyang - Overview
原本标题是:“Sa-Token登录详解”,写着写着,发现字数兜不住了,无奈还是拆开吧😂
前文讲了Sa-Token组件介绍,基本上重要的satoken
组件都过了一遍,最后也简单说明了一下组件注册管理机制。本文就satoken
登录前必须了解的进行说明。
环境声明:
Sa-Token官网,版本1.37.0
依赖:
sa-token-spring-boot-starter
sa-token-redisson-jackson
以上环境表示本次登录详解针对的是普通登录(非SSO
、OAuth
场景),集成redisson
存储satoken
数据。
登录应该如何设计
如果让我们来设计权限认证框架,或是说,让我们对使用过的权限认证框架进行抽象,使之能面对多种不同的登录鉴权场景,我们应该如何设计?
已知我登录送的用户id是1
,下面这张图展示的是satoken
在一次登录成功后在redis
中产生的k-v
数据。
对了,要结合一下我之前给出的satoken
配置。
1 | ############## Sa-Token 配置 (文档: https://sa-token.cc) ############## |
StpUtil与StpLogic
在讲登录流程前,一定要提到StpUtil
和StpLogic
。
官网:Sa-Token StpUtil - 鉴权工具类,StpUtil 是 Sa-Token 整体功能的核心,大多数功能均由此工具类提供。
可以看到StpUtil
只有一个私有构造器,私有构造器常用于哪些场景呢?
- 单例模式
- 工具类
- 不可实例化的抽象基类
- 枚举类型
- 安全性考虑
从StpUtil
的命名其实就可以确定了,工具类嘛!其中这个StpLogic
是Sa-Token
的核心,框架大多数功能均由此类提供具体逻辑实现。StpLogic
有三个子类用于集成JWT
,可参考官网Sa-Token 和 jwt
集成。
区别与选择,以下copy
于官网Sa-Token分布式Session会话解决方案。
要怎么解决这个问题呢?目前的主流方案有四种:
- Session同步:只要一个节点的数据发生了改变,就强制同步到其它所有节点
- Session粘滞:通过一定的算法,保证一个用户的所有请求都稳定的落在一个节点之上,对这个用户来讲,就好像还是在访问一个单机版的服务
- 建立会话中心:将Session存储在专业的缓存中间件上,使每个节点都变成了无状态服务,例如:Redis
- 颁发无状态token:放弃Session机制,将用户数据直接写入到令牌本身上,使会话数据做到令牌自解释,例如:jwt
本文使用方式3,集成Redis
,所以jwt
相关不多讲了。
前文也讲过SaManager
管理着Sa-Token
所有全局组件,其最重要体现就在这两个类中,往下看就对了。
Session会话
这里补充一个知识Sa-Token 中的 Session会话 模型详解,建议把原文看一下,思考一下为什么要这样设计,有没有其他设计思路。
- Account-Session 以账号 id 为主,只要 token 指向的账号 id 一致,那么对应的Session对象就一致
- Token-Session 以token为主,只要token不同,那么对应的Session对象就不同
- Custom-Session 以特定的key为主,不同key对应不同的Session对象,同样的key指向同一个Session对象
在写后面内容时发现这个必须要再强调一下,一定要把这个理解一下,很重要!
SaSession会话对象
那么satoken
具体是怎么做的?如何实现上面的Session
模型的呢?当然后面详解登录会讲,这里介绍一下最重要的两个类SaSession
和TokenSign
。
SaSession
会话对象,属性列表如下,看到tokenSignList
属性就应该明白了两者的关系。
TokenSign
Token
签名,我们实际使用的token
值就是这里的value
属性。
自定义登录
自定义登录如下,在登录之后StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
表示在当前tokenSession
中存储LoginUser
登录信息,方便后面直接通过缓存获取用户权限。
1 | public void login(LoginUser loginUser, DeviceTypeEnum deviceEnum) { |
缓存权限数据
前提:系统的权限架构是RBAC模型。建议查看参考:将权限数据放在缓存里,不看官网看下面也行🧐。
思路一:缓存 登录用户id-权限集合
用户登录成功后,缓存登录用户的权限集合。上面自定义登录的LoginUser
就是实现如下的Login
接口来缓存用户信息(包含角色、权限)的。
获取登录用户权限直接查缓存就好。
1 | /** |
思路二:缓存 登录用户id-角色集合-权限集合
用户登录成功后,缓存登录用户的角色集合。
获取登录用户权限需要:1、查缓存获取登录用户角色;2、通过角色缓存获取角色对应的权限集合。
那么角色缓存如何管理呢?以下仅供参考,写代码时再思考一下如何保证缓存一致性吧?
set
角色-权限集合
a、登录任何用户时,set
角色-权限集合
b、项目启动,set
所有角色-权限集合,多服务情况下避免多次重复set
就行
c、其他时刻,如管理角色时
update
角色-权限集合
a、通常在修改或删除角色对应权限,删除缓存/更新缓存
思路二同样可以使用上面的接口,只不过,getPermissions
不再是直接从登录用户id的缓存取了,而是通过角色缓存中获取。
区别
- 用户:U1,U2,U3。。。
- 角色:R1,R2,R3。。。
- 权限:P1,P2,P3。。。
思路一,直接取用户权限缓存
- 优点:直接简单;
- 缺点:在
RABC
模型下不存在用户-权限
直接关联,只存在用户-角色-权限
关联,也就是说在角色-权限
发生变动时,比较麻烦。1、更新权限保证最新,重新查询用户-角色-权限
,更新缓存。但这个有可能会导致缓存雪崩,因为角色-权限
更改了,那么所有登录用户们是被更改的角色时,都需要更新缓存,这是有一定风险的。2、权限不实时更新,只有下次登录权限才变化;
思路二,通过角色间接取权限缓存
- 优点:不同于思路一,在
角色-权限
发生变动时,只需要更新角色-权限
缓存就行。 - 缺点:查权限稍微麻烦一点,维护
用户-角色
、角色-权限
缓存需要一定设计。
剩下的留给下篇文章吧!
写在最后
拙作艰辛,字句心血,望诸君垂青,多予支持,不胜感激。
个人博客:无奈何杨(wnhyang)
个人语雀:wnhyang
共享语雀:在线知识共享
Github:wnhyang - Overview