介绍
对satoken的扩展和增强包括:安全处理,登录等实用类,部分代码来源于 RuoYi-Vue-Plus 代码在此基础上进行微调和二次封装。
注:satoken 存放redis里的序列化采用jackson,当cache序列化方式采用msgpack并不会影响satoken序列化方式。
依赖库
名称 | 描述 |
---|---|
sa-token-spring-boot-starter | |
sa-token-jwt | |
zebra-spring-boot-starter-cache | |
zebra-common-util | |
sa-token-redis-jackson |
快速开始
引入
xml
<dependency>
<groupId>io.github.zhanghongbin</groupId>
<artifactId>zebra-spring-boot-starter-satoken</artifactId>
</dependency>
satoken 默认配置
不需要使用者进行手动配置,默认配置如下:
yml
sa-token:
# 允许动态设置 token 有效期
dynamic-active-timeout: true
# 允许从 请求参数 读取 token
is-read-body: true
# 允许从 header 读取 token
is-read-header: true
# 关闭 cookie 鉴权 从根源杜绝 csrf 漏洞风险
is-read-cookie: false
# token前缀
token-prefix: "Bearer"
is-print: off
LoginHelper 类
提供基于satoken常用的登录操作,所有方法都为静态方法
名称 | 参数 | 返回值 | 描述 |
---|---|---|---|
login | LoginUser loginUser | 字符串 | 登录方法,参数为登录对象,返回登录token |
getLoginUser | 无 | LoginUser | 返回值为当前用户登录对象 |
getLoginUser | String token | LoginUser | 根据token返回值为登录对象 |
getLoginToken | 无 | String | 获取登录 token |
getUserId | 无 | 泛型T | 获取用户id |
getUserName | 无 | 字符串 | 获取用户账号 |
getUserType | 无 | 整型 | 获取用户类型 0 为 管理员 1 为普通用户 |
isSuperAdmin | T userId | boolean | 指定用户id是否是超级管理员 |
isSuperAdmin | 无 | boolean | 当前用户是否是超级管理员 |
isAdmin | 无 | boolean | 是否是管理员(只要是超级管理员或租户管理中的一个就视为管理员) |
isTenantAdmin | T userId, int userType | boolean | 是否是租户管理员 |
isTenantAdmin | 无 | boolean | 是否是租户管理员 |
isLogin | 无 | boolean | 检查当前用户是否已登录 |
示例:
LoginUser<T> 类,此类为泛型类,T 为用户id类型
java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginUser<T> implements Serializable {
/**
* 用户ID
*/
private T userId;
/**
* 用户账号
*/
private String userName;
/**
* 设备类型
*/
private String deviceType;
/**
* 0 为 管理员 1 为普通用户
*/
private int userType;
/**
* 获取登录id
*
* @return id
*/
public String getLoginId() {
if (deviceType == null) {
throw new IllegalArgumentException("设备类型不能为空");
}
if (userId == null) {
throw new IllegalArgumentException("用户ID不能为空");
}
return String.valueOf(userId) + ":" + deviceType;
}
使用示例: 直接使用
java
LoginUser<String> loginUser = new LoginUser();
继承并扩展字段
java
@Data
public class CurrentLoginUserDto extends LoginUser<String> implements Serializable {
private String nickName;
}
权限控制
权限异常码
错误码 | 异常类 | 描述 |
---|---|---|
-12 | NotPermissionException | 没有访问权限,请联系管理员授权 |
-13 | NotRoleException | 没有访问权限,请联系管理员授权 |
-11 | NotLoginException | 认证超时,无法访问系统资源 |
-10 | NotLoginException | 认证失败,无法访问系统资源 |
权限配置
名称 | 类型 | 描述 |
---|---|---|
enabled | boolean | true启用,false 禁用,默认为 false |
excludes | List | 排除地址示例: - /.html - /**/.html |
yml
zebra:
satoken:
security:
enabled:
excludes:
SaTokenPermission 类
SaTokenPermission 类,实现了StpInterface接口,具体权限使用查看satoken使用文档,使用示例: SaTokenPermission 构造函数两个参数分别为:Supplier 函数接口,分别获取权限列表和角色列表。
Supplier 函数接口 可以从数据库查或redis查询相关数据,具体看个人实现方式。
java
@Configuration
public class AdminConfig {
@Bean
public StpInterface stpInterface() {
return new SaTokenPermission(
() -> {
CurrentLoginUserDto loginUser = (CurrentLoginUserDto) LoginHelper.getLoginUser();
return new ArrayList<>(loginUser.getMenuPermission());
},
() -> {
CurrentLoginUserDto loginUser = (CurrentLoginUserDto) LoginHelper.getLoginUser();
return new ArrayList<>(loginUser.getRolePermission());
});
}
}
高级阶段
SaInterceptor 类
请求访问验证规则,以下是默认实现,先判断是否是同源,如果不是同源则调用 super.preHandle(request, response, handler)内部使用StpUtil.checkLogin()验证, 同源使用SaSameUtil.checkToken 进行验证,并发送一个参数为AccessType 事件。
使用者可以改写此验证规则,只需要继承SaInterceptor类,并实现 preHandle 方法,并注解@Component或@Bean
java
public class SecuritySaInterceptor extends SaInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String token = SaHolder.getRequest().getHeader(SaSameUtil.SAME_TOKEN);
if (StrUtil.isEmpty(token)) {
return super.preHandle(request, response, handler);
}
SaSameUtil.checkToken(token);
SpringUtil.getApplicationContext().publishEvent(AccessType.RPC);
return true;
}
}
AccessType 验证通过后的事件通知
当验证通过后,如果想做一些后续的操作,如缓存用户信息等。可以进行监听此事件,AccessType 为枚举类型,分别是HTTP和RPC,具体示例如下:
java
@Component
public class TestEvent {
@EventListener
public void onEvent(AccessType accessType) {
if (accessType == AccessType.HTTP) {
}
}
}