SaToken
轻量级 Java 权限认证框架,基于 RBAC 权限模型。
主要解决:登录认证、权限认证、单点登录、OAuth2.0、分布式Session会话、微服务网关鉴权 等一系列权限相关问题
登录与注销
Sa-Token 为这个账号创建了一个Token凭证,且通过 Cookie 上下文返回给了前端:
// 会话登录:参数填写要登录的账号id,建议的数据类型:long | int | String, 不可以传入复杂类型,如:User、Admin 等等
StpUtil.login(Object id);
// 当前会话注销登录
StpUtil.logout();
// 获取当前会话账号id, 如果未登录,则抛出异常:`NotLoginException`
StpUtil.getLoginId();
多账号认证
https://sa-token.com/doc.html#/up/many-account
在一个项目中设计两套账号体系,比如一个电商系统的 user表 和 admin表:
- 复制另写
StpUtil.java,更改字段LoginType; - 对
@SaCheckLogin(检验是否登录)、@SaCheckRole、@SaCheckPermission,指定type为上面修改的LoginType;
同端多登陆
假设不仅需要在后台同时集成两套账号,还需要在一个客户端同时登陆两套账号(业务场景举例:一个APP中可以同时登陆商家账号和用户账号)。在客户端会发生token覆盖,新登录的token会覆盖掉旧登录的token从而导致旧登录失效;
- 针对新写的
StpUserUtil,重写其TokenName;
public class StpUserUtil {
// 使用匿名子类 重写`stpLogic对象`的一些方法
public static StpLogic stpLogic = new StpLogic("user") {
// 重写 StpLogic 类下的 `splicingKeyTokenName` 函数,返回一个与 `StpUtil` 不同的token名称, 防止冲突
@Override
public String splicingKeyTokenName() {
return super.splicingKeyTokenName() + "-user";
}
// 同理你可以按需重写一些其它方法 ...
};
// ...
}
多类型/设备登录
强制注销 和 踢人下线 的区别在于:
- 强制注销等价于对方主动调用了注销方法,再次访问会提示:Token无效。
- 踢人下线不会清除Token信息,而是将其打上特定标记,再次访问会提示:Token已被踢下线。
踢人下线
StpUtil.kickout(10001); // 将指定账号踢下线
StpUtil.kickout(10001, "PC"); // 将指定账号指定端踢下线
StpUtil.kickoutByTokenValue("token"); // 将指定 Token 踢下线
强制注销
同设备互斥登录
在配置文件中,将 isConcurrent 配置为false,然后调用登录等相关接口时声明设备类型
调用此方法登录后,同设备的会被顶下线(不同设备不受影响),再次访问系统时会抛出 NotLoginException 异常(场景值=-4)
账号封禁
账号封禁
对于正在登录的账号,将其封禁并不会使它立即掉线,如果我们需要它即刻下线,可采用先踢再封禁的策略
- 校验封禁 和 登录(
StpUtil.login)两个动作分离成两个方法,登录时不再自动校验是否封禁
// 封禁指定账号1天时间,-1表示永久封禁
StpUtil.disable(10001, 86400);
// 校验指定账号是否已被封禁,如果被封禁则抛出异常 `DisableServiceException`
StpUtil.checkDisable(10001);
// 解除封禁
StpUtil.untieDisable(10001);
分类封禁
只禁止其访问部分服务,即禁止部分接口的调用。
// 封禁指定 用户 评论 能力,期限为 1天
StpUtil.disable(10001, "comment", 86400);
// 在评论接口,校验一下,会抛出异常:`DisableServiceException`,使用 e.getService() 可获取业务标识 `comment`
StpUtil.checkDisable(10001, "comment");
阶梯封禁
多次违规,封禁时间不一样,或者不同的违规行为,处罚力度不一样:
// 阶梯封禁,参数:封禁账号、封禁级别、封禁时间
StpUtil.disableLevel(10001, 3, 10000);
// 获取:指定账号封禁的级别 (如果此账号未被封禁则返回 -2)
StpUtil.getDisableLevel(10001);
// 判断:指定账号是否已被封禁到指定级别,返回 true 或 false
StpUtil.isDisableLevel(10001, 3);
// 校验:指定账号是否已被封禁到指定级别,如果已达到此级别(例如已被3级封禁,这里校验是否达到2级),则抛出异常 `DisableServiceException`
StpUtil.checkDisableLevel(10001, 2);
无Cookie模式
无 Cookie 模式:特指不支持 Cookie 功能的终端,通俗来讲就是我们常说的 —— 前后台分离模式,如app、小程序。
- 支持配置从
body、header、cookie中获取 token value;
解决方案:
- 不能后端控制写入了,就前端自己写入。(难点在后端如何将 Token 传递到前端)
- 每次请求不能自动提交了,那就手动提交。(难点在前端如何将 Token 传递到后端,同时后端将其读取出来)
后端如何将 Token 传递到前端
- 调用
StpUtil.login(id)进行登录。 - 调用
StpUtil.getTokenInfo()返回当前会话的 token 详细参数,前端人员将tokenName和tokenValue这两个值保存到本地。
前端将 token 提交到后端
将 token 塞到请求header里 ,格式为:{tokenName: tokenValue}:
- 使用 统一拦截器,注入该header字段;
模拟他人和身份切换
操作其它账号的api
StpUtil提供一系列方法指定 loginId 进行操作。
临时身份切换
// 将当前会话[身份临时切换]为其它账号(本次请求内有效)
StpUtil.switchTo(10044);
// 此时再调用此方法会返回 10044 (我们临时切换到的账号id)
StpUtil.getLoginId();
// 结束 [身份临时切换]
StpUtil.endSwitch();
临时Token令牌认证
[sa-token-temp临时认证模块] 已内嵌到核心包,无需引入其它依赖即可使用
- SaTempUtil 创建临时token后登陆的用户id与SaToken框架中StpUtil登陆的用户id并不关联,两者相互独立同时存在
// 根据 value 创建一个 token
String token = SaTempUtil.createToken("10014", 200);
// 解析 token 获取 value,并转换为指定类型
String value = SaTempUtil.parseToken(token, String.class);
// 获取指定 token 的剩余有效期,单位:秒
SaTempUtil.getTimeout(token);
// 删除指定 token
SaTempUtil.deleteToken(token);
数据存储
Sa-Token 数据存储有三大作用域,分别是:
SaStorage- 请求作用域:存储的数据只在一次请求内有效,无需处于登录状态;SaSession- 会话作用域:存储的数据在一次会话范围内有效,必须登录后;SaApplication- 全局作用域:存储的数据在全局范围内有效,无需处于登录状态。- 在 SaApplication 存储的数据在全局范围内有效,应用关闭后数据自动清除(如果集成了 Redis 那则是 Redis 关闭后数据自动清除)。
SaStorage storage = SaHolder.getStorage();
SaSession session = StpUtil.getSession();
SaApplication application = SaHolder.getApplication();
SaStorage
在 SaStorage 中存储的数据只在一次请求范围内有效,请求结束后数据自动清除。使用 SaStorage 时无需处于登录状态:
- 通过 HttpServletRequest 的
get/set方法;
Session会话
Session是会话中专业的数据缓存组件,通过 Session 我们可以很方便的缓存一些高频读写数据,提高程序性能,例如:
// 在登录时缓存user对象
StpUtil.getSession().set("user", user);
// 然后我们就可以在任意处使用这个user对象
SysUser user = (SysUser) StpUtil.getSession().get("user");
在 Sa-Token 中,Session 分为三种,分别是:
-
User-Session: 指的是框架为每个 账号id 分配的 Session -
Token-Session: 指的是框架为每个 token 分配的 Session -
Custom-Session: 指的是以一个 特定的值 作为SessionId,来分配的 Session
session的操作
session.get("name");
session.set("name", "zhang");
// 返回此 Session 会话上的底层数据对象(如果更新map里的值,请调用session.update()方法避免产生脏数据)
session.getDataMap();
// 将这个 Session 从持久库更新一下
session.update();
// 注销此 Session 会话 (从持久库删除此Session)
session.logout();
与HTTP Session区别
SaSession 与 HttpSession 没有任何关系,在
HttpSession上写入的值,在SaSession中无法取出
Servlet HttpSession的原理:由相应的 Servlet Container 实现(如 Tomcat等)
- 客户端每次与服务器第一次握手时,会被强制分配一个
[唯一id]作为身份标识,注入到 Cookie 之中, 之后每次发起请求时,客户端都要将它提交到后台,服务器根据[唯一id]找到每个请求专属的Session对象,维持会话;
Servlet HttpSession的缺点:
-
同一账号分别在PC、APP登录,会被识别为两个不相干的会话
-
一个设备难以同时登录两个账号
-
每次一个新的客户端访问服务器时,都会产生一个新的Session对象,即使这个客户端只访问了一次页面
-
在不支持Cookie的客户端下,这种机制会失效
为账号id分配的Session,叫做
User-Session
Sa-Token Session可以理解为 HttpSession 的升级版:
-
只在调用
StpUtil.login(id)登录会话时才会产生Session,节省性能 -
Session 是分配给账号id的,而不是分配给指定客户端的,也就是说在PC、APP上登录的同一账号所得到的Session也是同一个;
-
Sa-Token支持Cookie、Header、body三个途径提交Token
-
做到一个客户端同时登录多个账号
会遇到一些需要数据隔离的场景:
指定客户端超过两小时无操作就自动下线,如果两小时内有操作,就再续期两小时,直到新的两小时无操作。
- 放在 User-Session,则用户在PC端一直无操作,只要手机上用户还在不间断的操作,那PC端也不会过期!
Sa-Token针对会话登录,不仅为账号id分配了User-Session,同时还为每个token分配了不同的Token-Session
- 虽然两个设备登录的是同一账号,但是两个它们得到的token是不一样
User-Session
// 获取当前账号id的Session (必须是登录后才能调用)
StpUtil.getSession();
// 获取账号id为10001的Session
StpUtil.getSessionByLoginId(10001);
Token Session
默认场景下,只有登录后才能获取,在未登录场景下获取 Token-Session ,有两种方法:
方法一:将全局配置项 tokenSessionCheckLogin 改为 false; 方法二:使用匿名 Token-Session;
// 获取当前 Token 的 Token-Session 对象
StpUtil.getTokenSession();
// 获取指定 Token 的 Token-Session 对象
StpUtil.getTokenSessionByToken(token);
// 获取当前 Token 的匿名 Token-Session (可在未登录情况下使用的 Token-Session)
StpUtil.getAnonTokenSession();
自定义Session
以一个特定的值作为SessionId来分配的Session
// 查询指定key的Session是否存在
SaSessionCustomUtil.isExists("goods-10001");
// 获取指定key的Session,如果没有,则新建并返回
SaSessionCustomUtil.getSessionById("goods-10001");
// 删除指定key的Session
SaSessionCustomUtil.deleteSessionById("goods-10001");
分布式Session
Sa-Token 默认将数据保存在内存中,但重启后数据会丢失,且无法在分布式环境中共享数据。
目前的主流方案有四种:
-
Session同步:只要一个节点的数据发生了改变,就强制同步到其它所有节点(性能消耗太大,不太考虑)
-
Session粘滞:通过一定的算法,保证一个用户的所有请求都稳定的落在一个节点之上(从网关处动手,与框架无关)
-
建立会话中心:将Session存储在专业的缓存中间件上,使每个节点都变成了无状态服务,例如:
Redis -
颁发无状态token:放弃Session机制,将用户数据直接写入到令牌本身上,使会话数据做到令牌自解释,例如:
jwt
由于jwt模式不在服务端存储数据,对于比较复杂的业务可能会功能受限,因此更加推荐使用方案三
集成Redis:
<!-- Sa-Token 整合 Redis (使用 jdk 默认序列化方式, 2选1 ) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis</artifactId>
<version>1.33.0</version>
</dependency>
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式, 2选1 ) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.33.0</version>
</dependency>
<!-- 提供Redis连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
application.yml 配置
spring:
# redis配置
redis:
# Redis数据库索引(默认为0)
database: 1
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
# password:
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
SaApplication
在 SaApplication 存储的数据在全局范围内有效,应用关闭后数据自动清除(如果集成了 Redis 那则是 Redis 关闭后数据自动清除)。使用 SaApplication 时无需处于登录状态。
SaApplication application = SaHolder.getApplication();
application.get("key"); // 取值
application.set("key", "value"); // 写值
application.delete("key"); // 删值
SaTokenDao的实现
SaTokenDao 是数据持久层接口,负责所有会话数据的底层写入和读取,存储的内容
-
Session 本身的 set 的值,不推荐使用,不同的微服务不建议使用其进行数据共享;
-
tokenValue到loginId的映射关系; -
标记该token是否注销、强制下线等场景;
Sa-Token默认的Redis集成方式会把权限数据和业务缓存(spring redis template)放在一起,Sa-Token-Alone-Redis 仅对以下插件有 Redis 分离效果:
-
sa-token-dao-redis :Redis集成包,使用 jdk 默认序列化方式。
-
sa-token-dao-redis-jackson :Redis集成包,使用 jackson 序列化方式。
-
sa-token-dao-redis-fastjson :Redis集成包,使用 fastjson 序列化方式。
-
sa-token-dao-redis-fastjson2 :Redis集成包,使用 fastjson2 序列化方式。
<!-- Sa-Token插件:权限缓存与业务缓存分离 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-alone-redis</artifactId>
<version>1.33.0</version>
</dependency>
application.yml中增加配置
# Sa-Token 配置
sa-token:
# Token名称
token-name: satoken
# Token有效期
timeout: 2592000
# Token风格
token-style: uuid
# 配置 Sa-Token 单独使用的 Redis 连接
alone-redis:
# Redis数据库索引(默认为0)
database: 2
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password:
# 连接超时时间
timeout: 10s
spring:
# 配置业务使用的 Redis 连接
redis:
# Redis数据库索引(默认为0)
database: 0
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password:
# 连接超时时间
timeout: 10s
JWT 集成
<!-- Sa-Token 整合 jwt 显式依赖 hutool-jwt 5.7.14 版本 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jwt</artifactId>
<version>1.33.0</version>
</dependency>
在 application.yml 配置文件中配置 jwt 生成秘钥:
注入:
@Configuration
public class SaTokenConfigure {
@Bean
public StpLogic getStpLogicJwt() {
// Sa-Token 整合 jwt (Simple 简单模式)
return new StpLogicJwtForSimple();
// Sa-Token 整合 jwt (Mixin 混入模式)
return new StpLogicJwtForMixin();
// Sa-Token 整合 jwt (Stateless 无状态模式)
return new StpLogicJwtForStateless();
}
}
注入不同模式的对比
注入不同模式会让框架具有不同的行为策略,以下是三种模式的差异点(为方便叙述,以下比较以同时引入 jwt 与 Redis 作为前提):
| 功能点 | Simple 简单模式 | Mixin 混入模式 | Stateless 无状态模式 |
|---|---|---|---|
| Token风格 | jwt风格 | jwt风格 | jwt风格 |
| 登录数据存储 | Redis中 | Token中 | Token中 |
| Session存储 | Redis中 | Redis中 | 无Session |
| 注销下线 | 前后端双清数据 | 前后端双清数据 | 前端清除数据 |
| 踢人下线API | 支持 | 不支持 | 不支持 |
| 顶人下线API | 支持 | 不支持 | 不支持 |
| 登录认证 | 支持 | 支持 | 支持 |
| 角色认证 | 支持 | 支持 | 支持 |
| 权限认证 | 支持 | 支持 | 支持 |
| timeout 有效期 | 支持 | 支持 | 支持 |
| activity-timeout 有效期 | 支持 | 支持 | 不支持 |
| id反查Token | 支持 | 支持 | 不支持 |
| 会话管理 | 支持 | 部分支持 | 不支持 |
| 注解鉴权 | 支持 | 支持 | 支持 |
| 路由拦截鉴权 | 支持 | 支持 | 支持 |
| 账号封禁 | 支持 | 支持 | 不支持 |
| 身份切换 | 支持 | 支持 | 支持 |
| 二级认证 | 支持 | 支持 | 支持 |
| 模式总结 | Token风格替换 | jwt 与 Redis 逻辑混合 | 完全舍弃Redis,只用jwt |
在多账户模式中集成 jwt
sa-token-jwt 插件默认只为 StpUtil 注入 StpLogicJwtFoxXxx 实现,自定义的 StpUserUtil 是不会自动注入的,我们需要帮其手动注入:
/**
* 为 StpUserUtil 注入 StpLogicJwt 实现
*/
@Autowired
public void setUserStpLogic() {
StpUserUtil.setStpLogic(new StpLogicJwtForSimple(StpUserUtil.TYPE));
}
SaTokenContext的实现
目前 Sa-Token 仅对 SpringBoot、SpringMVC、WebFlux、Solon 等部分 Web 框架制作了 Starter 集成包, 如果我们使用的 Web 框架不在上述列表之中,则需要自定义 SaTokenContext 接口的实现完成整合工作。
从 HttpServletRequest 中读取 Token,但不是所有框架都具有 HttpServletRequest 对象,例如在 WebFlux 中,只有 ServerHttpRequest, 在一些其它Web框架中,可能连 Request 的概念都没有。
SaTokenContext定义接口,sa-token-spring-boot-starter集成包中已经内置了SaTokenContext的实现:SaTokenContextForSpring;- 其它框架,需要自定义实现
SaTokenContext。
权限
权限认证
接口定义
StpInterface 提供获取角色和获取权限的接口定义,根据自己的业务逻辑进行重写。
loginType字段见多账户认证部分;
public interface StpInterface {
/** 返回指定账号账号类型的id的所拥有的权限码集合 */
public List<String> getPermissionList(Object loginId, String loginType);
/** 返回指定账号账号类型的id所拥有的角色标识集合 */
public List<String> getRoleList(Object loginId, String loginType);
}
权限/角色校验
StpUtil提供工具,也可通过注解+拦截器实现;
// 获取:当前账号所拥有的权限集合
StpUtil.getPermissionList();
// 判断:当前账号是否含有指定权限, 返回 true 或 false
StpUtil.hasPermission("user.add");
// 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException
StpUtil.checkPermission("user.add");
// 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过]
StpUtil.checkPermissionAnd("user.add", "user.delete", "user.get");
// 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
StpUtil.checkPermissionOr("user.add", "user.delete", "user.get");
// 获取:当前账号所拥有的角色集合
StpUtil.getRoleList();
// 判断:当前账号是否拥有指定角色, 返回 true 或 false
StpUtil.hasRole("super-admin");
// 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
StpUtil.checkRole("super-admin");
// 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
StpUtil.checkRoleAnd("super-admin", "shop-admin");
// 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
StpUtil.checkRoleOr("super-admin", "shop-admin");
二级认证
在某些敏感操作下,我们需要对已登录的会话进行二次验证。即:在已登录会话的基础上,进行再次验证,提高会话的安全性。
Sa-Token中进行二级认证非常简单,只需要使用以下API:
- 且可以指定一个业务标识来分辨不同的业务线:
// 在当前会话 开启二级认证,业务标识为client,时间为600秒
StpUtil.openSafe("client", 600);
// 获取:当前会话是否已完成指定业务的二级认证
StpUtil.isSafe("client");
// 校验:当前会话是否已完成指定业务的二级认证 ,如未认证则抛出异常
StpUtil.checkSafe("client");
// 获取当前会话指定业务二级认证剩余有效时间 (单位: 秒, 返回-2代表尚未通过二级认证)
StpUtil.getSafeTime("client");
// 在当前会话 结束指定业务标识的二级认证
StpUtil.closeSafe("client");
调用步骤:
- 前端调用
deleteProject接口,尝试删除仓库。 - 后端校验会话尚未完成二级认证,返回:
仓库删除失败,请完成二级认证后再次访问接口。 - 前端将信息提示给用户,用户输入密码,调用
openSafe接口。 - 后端比对用户输入的密码,完成二级认证,有效期为:120秒。
- 前端在 120 秒内再次调用
deleteProject接口,尝试删除仓库。 - 后端校验会话已完成二级认证,返回:
仓库删除成功。
单服务路由拦截鉴权
基于 StpUtil.checkLogin() 的登录校验拦截器,并且排除了/user/doLogin接口用来开放登录:
- 内部通过
SaHolder获取request信息,进行路由匹配; - 更多用法参考:https://sa-token.dev33.cn/doc.html#/use/route-check
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
// 注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器
registry.addInterceptor(new SaInterceptor(handle -> {
// 指定一条 match 规则
SaRouter
.match("/**") // 拦截的 path 列表,可以写多个 */
.notMatch("/user/doLogin") // 排除掉的 path 列表,可以写多个
.check(r -> StpUtil.checkLogin()); // 要执行的校验动作,可以写完整的 lambda 表达式
// 根据路由划分模块,不同模块不同鉴权
SaRouter.match("/user/**", r -> StpUtil.checkPermission("user"));
SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin"));
})).addPathPatterns("/**");
}
}
网关统一鉴权
以[SpringCloud Gateway]为例
<!-- Sa-Token 权限认证(Reactor响应式集成), 在线文档:https://sa-token.cc -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-reactor-spring-boot-starter</artifactId>
<version>1.33.0</version>
</dependency>
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.33.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
实现鉴权接口
关于数据的获取,建议以下方案三选一:
- 在网关处集成ORM框架,直接从数据库查询数据
- 先从Redis中获取数据,获取不到时走ORM框架查询数据库
- 先从Redis中获取缓存数据,获取不到时走RPC调用子服务 (专门的权限数据提供服务) 获取
/**
* 自定义权限验证接口扩展
*/
@Component
public class StpInterfaceImpl implements StpInterface {
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
// 返回此 loginId 拥有的权限列表
return ...;
}
@Override
public List<String> getRoleList(Object loginId, String loginType) {
// 返回此 loginId 拥有的角色列表
return ...;
}
}
注册全局过滤器
@Configuration
public class SaTokenConfigure {
// 注册 Sa-Token全局过滤器
@Bean
public SaReactorFilter getSaReactorFilter() {
return new SaReactorFilter()
// 拦截全部path
.addInclude("/**")
// 开放地址
.addExclude("/favicon.ico")
// 鉴权方法:每次访问进入
.setAuth(obj -> {
// 登录校验 -- 拦截所有路由,并排除/user/doLogin 用于开放登录
SaRouter.match("/**", "/user/doLogin", r -> StpUtil.checkLogin());
// 权限认证 -- 不同模块, 校验不同权限
SaRouter.match("/user/**", r -> StpUtil.checkPermission("user"));
SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin"));
SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods"));
SaRouter.match("/orders/**", r -> StpUtil.checkPermission("orders"));
// 更多匹配 ... */
})
// 异常处理方法:每次setAuth函数出现异常时进入
.setError(e -> {
return SaResult.error(e.getMessage());
});
}
}
单点登录(TODO)
其它
全局监听器
提供一种侦听器机制,通过注册侦听器,你可以订阅框架的一些关键性事件,例如:用户登录、退出、被踢下线等。
- 同步调用,即在用户登录的函数处理过程中调用监听器的事件处理;
- 进程级别,不提供分布式场景下的监听处理;
工具类
https://sa-token.dev33.cn/doc.html#/more/common-action
配置
FAQ
依赖引入说明
sa-token-spring-boot-starter
- 内部基础服务一般使用SpringBoot默认的web模块:SpringMVC, 基于Servlet模型的,引入
sa-token-spring-boot-starter
sa-token-reactor-spring-boot-starter
SpringCloud Gateway、ShenYu等基于Reactor模型引入的是:sa-token-reactor-spring-boot-starter
反向代理 uri 丢失的问题
使用 request.getRequestURL() 可获取当前程序所在外网的访问地址,在 Sa-Token 中,其 SaHolder.getRequest().getUrl() 也正是借助此API完成, 有很多模块都用到了这个能力,比如SSO单点登录。
- 部署时使用 Nginx 做了一层反向代理后,获取到的 Url 路径可能会出问题;
https://sa-token.dev33.cn/doc.html#/fun/curr-domain