1.Sa-Token 介绍
这是我截取官网的一些介绍,写的比较好
sa-token官网:https://sa-token.cc/doc.html#/
Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证、权限认证、单点登录、OAuth2.0、分布式Session会话、微服务网关鉴权 等一系列权限相关问题。
2.Sa-Token 功能一览
Sa-Token 目前主要五大功能模块:登录认证、权限认证、单点登录、OAuth2.0、微服务鉴权。
登录认证 —— 单端登录、多端登录、同端互斥登录、七天内免登录。
权限认证 —— 权限认证、角色认证、会话二级认证。
踢人下线 —— 根据账号id踢人下线、根据Token值踢人下线。
注解式鉴权 —— 优雅的将鉴权与业务代码分离。
路由拦截式鉴权 —— 根据路由拦截鉴权,可适配 restful 模式。
Session会话 —— 全端共享Session,单端独享Session,自定义Session,方便的存取值。
持久层扩展 —— 可集成 Redis,重启数据不丢失。
前后台分离 —— APP、小程序等不支持 Cookie 的终端也可以轻松鉴权。
Token风格定制 —— 内置六种 Token 风格,还可:自定义 Token 生成策略。
记住我模式 —— 适配 [记住我] 模式,重启浏览器免验证。
二级认证 —— 在已登录的基础上再次认证,保证安全性。
模拟他人账号 —— 实时操作任意用户状态数据。
临时身份切换 —— 将会话身份临时切换为其它账号。
同端互斥登录 —— 像QQ一样手机电脑同时在线,但是两个手机上互斥登录。
账号封禁 —— 登录封禁、按照业务分类封禁、按照处罚阶梯封禁。
密码加密 —— 提供基础加密算法,可快速 MD5、SHA1、SHA256、AES 加密。
会话查询 —— 提供方便灵活的会话查询接口。
Http Basic认证 —— 一行代码接入 Http Basic、Digest 认证。
全局侦听器 —— 在用户登陆、注销、被踢下线等关键性操作时进行一些AOP操作。
全局过滤器 —— 方便的处理跨域,全局设置安全响应头等操作。
多账号体系认证 —— 一个系统多套账号分开鉴权(比如商城的 User 表和 Admin 表)
单点登录 —— 内置三种单点登录模式:同域、跨域、同Redis、跨Redis、前后端分离等架构都可以搞定。
单点注销 —— 任意子系统内发起注销,即可全端下线。
OAuth2.0认证 —— 轻松搭建 OAuth2.0 服务,支持openid模式 。
分布式会话 —— 提供共享数据中心分布式会话方案。
微服务网关鉴权 —— 适配Gateway、ShenYu、Zuul等常见网关的路由拦截认证。
RPC调用鉴权 —— 网关转发鉴权,RPC调用鉴权,让服务调用不再裸奔
临时Token认证 —— 解决短时间的 Token 授权问题。
独立Redis —— 将权限缓存与业务缓存分离。
Quick快速登录认证 —— 为项目零代码注入一个登录页面。
标签方言 —— 提供 Thymeleaf 标签方言集成包,提供 beetl 集成示例。
jwt集成 —— 提供三种模式的 jwt 集成方案,提供 token 扩展参数能力。
RPC调用状态传递 —— 提供 dubbo、grpc 等集成包,在RPC调用时登录状态不丢失。
参数签名 —— 提供跨系统API调用签名校验模块,防参数篡改,防请求重放。
自动续签 —— 提供两种Token过期策略,灵活搭配使用,还可自动续签。
开箱即用 —— 提供SpringMVC、WebFlux、Solon 等常见框架集成包,开箱即用。
最新技术栈 —— 适配最新技术栈:支持 SpringBoot 3.x,jdk 17。
3.使用
1.maven坐标导入
<!-- Sa-Token 权限认证,在线文档:https://sa-token.cc -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.44.0</version>
</dependency>
2.yml配置
server:
# 端口
port: 8081
############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
sa-token:
# token 名称(同时也是 cookie 名称)
token-name: satoken
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
timeout: 2592000
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
active-timeout: -1
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
is-share: false
# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
token-style: uuid
# 是否输出操作日志
is-log: true
3.常用命令
1、常规操作
StpUtil.getStpLogic(); // 获取底层 StpLogic 对象。
StpUtil.setStpLogic(newStpLogic); // 安全的重置底层 StpLogic 引用。
StpUtil.getLoginType(); // 获取账号类型 (例如:login、user、admin、teacher、student等等)。
StpUtil.getTokenName(); // 获取 Token 的名称
StpUtil.getTokenValue(); // 获取本次请求前端提交的 Token。
StpUtil.getTokenValueNotCut(); // 获取本次请求前端提交的 Token (不裁剪前缀) 。
StpUtil.setTokenValue(tokenValue); // 在当前会话中写入 Token 值。
StpUtil.setTokenValue(tokenValue, timeout); // 在当前会话中写入 Token 值,并指定 Cookie 有效期。
StpUtil.getTokenInfo(); // 获取当前 Token 的详细参数。
2、登录相关
StpUtil.login(10001); // 会话登录
StpUtil.login(10001, "APP"); // 会话登录,并指定设备类型
StpUtil.login(10001, true); // 会话登录,并指定是否 [记住我]
StpUtil.login(10001, loginParameter); // 会话登录,并指定所有登录参数Model
StpUtil.createLoginSession(10001); // 创建指定账号id的登录会话,此方法不会将 Token 注入到上下文
StpUtil.createLoginSession(10001, loginParameter); // 创建指定账号id的登录会话,此方法不会将 Token 注入到上下文
示例
// SaLoginParameter 配置登录相关参数
StpUtil.login(10001, new SaLoginParameter()
.setDeviceType("PC") // 此次登录的客户端设备类型, 一般用于完成 [同端互斥登录] 功能
.setDeviceId("xxxxxxxxx") // 此次登录的客户端设备ID, 登录成功后该设备将标记为可信任设备
.setIsLastingCookie(true) // 是否为持久Cookie(临时Cookie在浏览器关闭时会自动删除,持久Cookie在重新打开后依然存在)
.setTimeout(60 * 60 * 24 * 7) // 指定此次登录 token 的有效期, 单位:秒,-1=永久有效
.setActiveTimeout(60 * 60 * 24 * 7) // 指定此次登录 token 的最低活跃频率, 单位:秒,-1=不进行活跃检查
.setIsConcurrent(true) // 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
.setIsShare(false) // 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个token, 为 false 时每次登录新建一个 token)
.setMaxLoginCount(12) // 同一账号最大登录数量,-1代表不限 (只有在 isConcurrent=true, isShare=false 时此配置项才有意义)
.setMaxTryTimes(12) // 在每次创建 token 时的最高循环次数,用于保证 token 唯一性(-1=不循环尝试,直接使用)
.setExtra("key", "value") // 记录在 Token 上的扩展参数(只在 jwt 模式下生效)
.setToken("xxxx-xxxx-xxxx-xxxx") // 预定此次登录的生成的Token
.setIsWriteHeader(false) // 是否在登录后将 Token 写入到响应头
.setTerminalExtra("key", "value")// 本次登录挂载到 SaTerminalInfo 的自定义扩展数据
);
3、注销相关
StpUtil.logout(); // 会话注销
StpUtil.logout(10001); // 会话注销,根据账号id
StpUtil.logout(10001, "PC"); // 会话注销,根据账号id 和 设备类型
StpUtil.logoutByTokenValue(token); // 指定 Token 强制注销
StpUtil.kickout(10001); // 踢人下线,根据账号id
StpUtil.kickout(10001, "PC"); // 踢人下线,根据账号id 和 设备类型
StpUtil.kickoutByTokenValue(token); // 踢人下线,根据token
4、会话查询
StpUtil.isLogin(); // 当前会话是否已经登录
StpUtil.checkLogin(); // 检验当前会话是否已经登录,如未登录,则抛出异常
StpUtil.getLoginId(); // 获取当前会话账号id, 如果未登录,则抛出异常
StpUtil.getLoginId(defaultValue); // 获取当前会话账号id, 如果未登录,则返回默认值
StpUtil.getLoginIdDefaultNull(); // 获取当前会话账号id, 如果未登录,则返回null
StpUtil.getLoginIdAsString(); // 获取当前会话账号id, 并转换为String类型
StpUtil.getLoginIdAsInt(); // 获取当前会话账号id, 并转换为int类型
StpUtil.getLoginIdAsLong(); // 获取当前会话账号id, 并转换为long类型
StpUtil.getLoginIdByToken(token); // 获取指定Token对应的账号id,如果未登录,则返回 null
StpUtil.getExtra(key); // 获取当前 Token 的扩展信息(此函数只在jwt模式下生效)
StpUtil.getExtra(token, key); // 获取指定 Token 的扩展信息(此函数只在jwt模式下生效)
5、Session 相关
// Account-Session 相关
StpUtil.getSession(); // 获取当前会话的Session,如果Session尚未创建,则新建并返回
StpUtil.getSession(true); // 获取当前会话的Session, 如果Session尚未创建,isCreate=是否新建并返回
StpUtil.getSessionByLoginId(10001); // 获取指定账号id的Session,如果Session尚未创建,则新建并返回
StpUtil.getSessionByLoginId(10001, true); // 获取指定账号id的Session, 如果Session尚未创建,isCreate=是否新建并返回
// Token-Session 相关
StpUtil.getTokenSession(); // 获取当前会话的Session,如果Session尚未创建,则新建并返回
StpUtil.getTokenSessionByToken(token); // 获取指定Token-Session,如果Session尚未创建,则新建并返回
StpUtil.getAnonTokenSession(); // 获取当前匿名 Token-Session (可在未登录情况下使用的Token-Session)
// 其它
StpUtil.getSessionBySessionId("xxxx-xxxx-xxxx"); // 获取指定key的Session, 如果Session尚未创建,则返回 null
StpUtil.isTrustDeviceId(123456, "xxxxxxxxxxxxxxxxxxxxxxxx"); // 判断对于指定 loginId 来讲,指定设备 id 是否为可信任设备
6、Token有效期相关
// Token 最低活跃频率
StpUtil.getTokenActiveTimeout(); // 获取当前 token 距离被冻结还剩多少时间 (单位: 秒)
StpUtil.getTokenLastActiveTime(); // 获取当前 token 最后活跃时间
StpUtil.checkActiveTimeout(); // 检查当前token 是否已经被冻结,如果是则抛出异常
StpUtil.updateLastActiveToNow(); // 续签当前token:(将 [最后操作时间] 更新为当前时间戳)
// Token 有效期
StpUtil.getTokenTimeout(); // 获取当前登录者的 token 剩余有效时间 (单位: 秒)
StpUtil.getSessionTimeout(); // 获取当前登录者的 Account-Session 剩余有效时间 (单位: 秒)
StpUtil.getTokenSessionTimeout(); // 获取当前 Token-Session 剩余有效时间 (单位: 秒)
StpUtil.renewTimeout(timeout); // 对当前 Token 的 timeout 值进行续期
StpUtil.renewTimeout(token, timeout); // 对指定 Token 的 timeout 值进行续期
7、角色认证
StpUtil.getRoleList(); // 获取:当前账号的角色集合
StpUtil.getRoleList(10001); // 获取:指定账号的角色集合
StpUtil.hasRole(role); // 判断:当前账号是否拥有指定角色, 返回true或false
StpUtil.hasRole(loginId, role); // 判断:指定账号是否含有指定角色标识, 返回true或false
StpUtil.hasRoleAnd(...roleArray); // 判断:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
StpUtil.hasRoleOr(...roleArray); // 判断:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
StpUtil.checkRole(role); // 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
StpUtil.checkRoleAnd(...roleArray); // 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
StpUtil.checkRoleOr(...roleArray); // 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
8、权限认证
StpUtil.getPermissionList(); // 获取:当前账号的权限集合
StpUtil.getPermissionList(10001); // 获取:指定账号的权限集合
StpUtil.hasPermission(permission); // 判断:当前账号是否拥有指定权限, 返回true或false
StpUtil.hasPermission(loginId, permission); // 判断:指定账号是否含有指定权限标识, 返回true或false
StpUtil.hasPermissionAnd(...permissionArray); // 判断:当前账号是否含有指定权限标识 [指定多个,必须全部验证通过]
StpUtil.hasPermissionOr(...permissionArray); // 判断:当前账号是否含有指定权限标识 [指定多个,只要其一验证通过即可]
StpUtil.checkPermission(permission); // 校验:当前账号是否含有指定权限标识, 如果验证未通过,则抛出异常: NotPermissionException
StpUtil.checkPermissionAnd(...permissionArray); // 校验:当前账号是否含有指定权限标识 [指定多个,必须全部验证通过]
StpUtil.checkPermissionOr(...permissionArray); // 校验:当前账号是否含有指定权限标识 [指定多个,只要其一验证通过即可]
9、id 反查 Token
StpUtil.getTokenValueByLoginId(10001); // 获取指定账号id的tokenValue
StpUtil.getTokenValueByLoginId(10001, "PC"); // 获取指定账号id指定设备类型端的tokenValue
StpUtil.getTokenValueListByLoginId(10001); // 获取指定账号id的tokenValue集合
StpUtil.getTokenValueListByLoginId(10001, "APP"); // 获取指定账号id指定设备类型端的tokenValue 集合
StpUtil.getTerminalListByLoginId(10001); // 获取指定账号 id 已登录设备信息集合
StpUtil.getTerminalListByLoginId(10001, "PC"); // 获取指定账号 id 指定设备类型端的已登录设备信息集合
StpUtil.getLoginDevice(); // 返回当前会话的登录设备类型
StpUtil.getLoginDeviceByToken(xxx); // 返回任意 token 的登录设备类型
10、会话管理
StpUtil.searchTokenValue(keyword, start, size, sortType); // 根据条件查询Token
StpUtil.searchSessionId(keyword, start, size, sortType); // 根据条件查询SessionId
StpUtil.searchTokenSessionId(keyword, start, size, sortType); // 根据条件查询Token专属Session的Id
11、账号封禁
StpUtil.disable(10001, 1200); // 封禁:指定账号 指定时间(单位s)
StpUtil.isDisable(10001); // 判断:指定账号是否已被封禁 (true=已被封禁, false=未被封禁)
StpUtil.checkDisable(10001); // 校验:指定账号是否已被封禁,如果被封禁则抛出异常 `DisableServiceException`
StpUtil.getDisableTime(10001); // 获取:指定账号剩余封禁时间,单位:秒(-1=永久封禁,-2=未被封禁)
StpUtil.untieDisable(loginId); // 解封:指定账号
12、分类封禁
StpUtil.disable(10001, "<业务标识>", 86400); // 封禁:指定账号的指定服务 指定时间(单位s)
StpUtil.isDisable(10001, "<业务标识>"); // 判断:指定账号的指定服务 是否已被封禁 (true=已被封禁, false=未被封禁)
StpUtil.checkDisable(10001, "<业务标识>"); // 校验:指定账号的指定服务 是否已被封禁,如果被封禁则抛出异常 `DisableServiceException`
StpUtil.getDisableTime(10001, "<业务标识>"); // 获取:指定账号的指定服务 剩余封禁时间,单位:秒(-1=永久封禁,-2=未被封禁)
StpUtil.untieDisable(loginId, "<业务标识>"); // 解封:指定账号的指定服务
13、阶梯封禁
StpUtil.disableLevel(10001, "comment", 3, 10000); // 分类阶梯封禁,参数:封禁账号、封禁服务、封禁级别、封禁时间
StpUtil.getDisableLevel(10001, "comment"); // 获取:指定账号的指定服务 封禁的级别 (如果此账号未被封禁则返回 -2)
StpUtil.isDisableLevel(10001, "comment", 3); // 判断:指定账号的指定服务 是否已被封禁到指定级别,返回 true 或 false
StpUtil.checkDisableLevel(10001, "comment", 2); // 校验:指定账号的指定服务 是否已被封禁到指定级别(例如 comment服务 已被3级封禁,这里校验是否达到2级),如果已达到此级别,则抛出异常
14、身份切换
StpUtil.switchTo(10044); // 临时切换身份为指定账号id
StpUtil.endSwitch(); // 结束临时切换身份
StpUtil.isSwitch(); // 当前是否正处于[身份临时切换]中
StpUtil.switchTo(10044, () -> {}); // 在一个代码段里方法内,临时切换身份为指定账号id
15、二级认证
StpUtil.openSafe(safeTime); // 在当前会话 开启二级认证
StpUtil.isSafe(); // 当前会话 是否处于二级认证时间内
StpUtil.checkSafe(); // 检查当前会话是否已通过二级认证,如未通过则抛出异常
StpUtil.getSafeTime(); // 获取当前会话的二级认证剩余有效时间 (单位: 秒, 返回-2代表尚未通过二级认证)
StpUtil.closeSafe(); // 在当前会话 结束二级认证
16、带有业务标识的二级认证
StpUtil.openSafe("<业务标识>", safeTime); // 在当前会话 指定业务标识开启二级认证
StpUtil.isSafe("<业务标识>"); // 当前会话 指定业务标识是否处于二级认证时间内
StpUtil.checkSafe("<业务标识>"); // 检查当前会话,指定业务标识是否已通过二级认证,如未通过则抛出异常
StpUtil.getSafeTime("<业务标识>"); // 获取当前会话的指定业务标识二级认证剩余有效时间 (单位: 秒, 返回-2代表尚未通过二级认证)
StpUtil.closeSafe("<业务标识>"); // 在当前会话 结束指定业务标识二级认证
4.鉴权
package com.yy.yiyang.config;
import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpUtil;
import com.yy.yiyang.domain.pojo.UserAccount;
import com.yy.yiyang.service.UserAccountService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;
import com.yy.yiyang.handler.BusinessException;
import java.util.ArrayList;
import java.util.List;
import static com.yy.yiyang.constant.ResponseEnum.USER_ISNOT_ROLE;
import static com.yy.yiyang.constant.UserTypeConstant.*;
/**
* 自定义权限加载接口实现类
*/
@Component // 保证此类被 SpringBoot 扫描,完成 Sa-Token 的自定义权限验证扩展
public class StpInterfaceImpl implements StpInterface {
@Resource
private UserAccountService userAccountService;
/**
* 返回一个账号所拥有的权限码集合
*/
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
// 本 list 仅做模拟,实际项目中要根据具体业务逻辑来查询权限
List<String> list = new ArrayList<String>();
return list;
}
/**
* 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验)
*/
@Override
public List<String> getRoleList(Object loginId, String loginType) {
// 本 list 仅做模拟,实际项目中要根据具体业务逻辑来查询角色
List<String> list = new ArrayList<String>();
int userId = StpUtil.getLoginIdAsInt(); // 假设 loginId 是用户ID
UserAccount user = userAccountService.getById(userId);
int role =user.getUserType();
if (role ==USER_TYPE_ADMIN){
list.add("admin");
return list;
}
// 如果是老人,则添加老人角色
if (role == USER_TYPE_ELDER) {
list.add("elder");
return list;
}
// 如果是医生,则添加医生角色
if (role == USER_TYPE_DOCTOR) {
list.add("doctor");
return list;
}
throw new BusinessException(USER_ISNOT_ROLE);
}
}
5.集成redis
maven
<!-- Sa-Token 整合 RedisTemplate -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-template</artifactId>
<version>1.44.0</version>
</dependency>
<!-- 提供 Redis 连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- Sa-Token 整合 Fastjson -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-fastjson</artifactId>
<version>1.44.0</version>
</dependency>
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
6.同时它也支持注解的形式
maven
<!-- Sa-Token 整合 SpringAOP 实现注解鉴权 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-aop</artifactId>
<version>1.44.0</version>
</dependency>
主要的注解
@SaCheckLogin: 登录校验 —— 只有登录之后才能进入该方法。
@SaCheckRole("admin"): 角色校验 —— 必须具有指定角色标识才能进入该方法。
@SaCheckPermission("user:add"): 权限校验 —— 必须具有指定权限才能进入该方法。
@SaCheckSafe: 二级认证校验 —— 必须二级认证之后才能进入该方法。
@SaCheckHttpBasic: HttpBasic校验 —— 只有通过 HttpBasic 认证后才能进入该方法。
@SaCheckHttpDigest: HttpDigest校验 —— 只有通过 HttpDigest 认证后才能进入该方法。
@SaCheckDisable("comment"):账号服务封禁校验 —— 校验当前账号指定服务是否被封禁。
@SaCheckSign:API 签名校验 —— 用于跨系统的 API 签名参数校验。
@SaIgnore:忽略校验 —— 表示被修饰的方法或类无需进行注解鉴权和路由拦截器鉴权。
Sa-Token 使用全局拦截器完成注解鉴权功能,为了不为项目带来不必要的性能负担,拦截器默认处于关闭状态
因此,为了使用注解鉴权,你必须手动将 Sa-Token 的全局拦截器注册到你项目中
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
// 注册 Sa-Token 拦截器,打开注解式鉴权功能
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器,打开注解式鉴权功能
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
}
}