Skip to content

介绍

对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常用的登录操作,所有方法都为静态方法

名称参数返回值描述
loginLoginUser loginUser字符串登录方法,参数为登录对象,返回登录token
getLoginUserLoginUser返回值为当前用户登录对象
getLoginUserString tokenLoginUser根据token返回值为登录对象
getLoginTokenString获取登录 token
getUserId泛型T获取用户id
getUserName字符串获取用户账号
getUserType整型获取用户类型 0 为 管理员 1 为普通用户
isSuperAdminT userIdboolean指定用户id是否是超级管理员
isSuperAdminboolean当前用户是否是超级管理员
isAdminboolean是否是管理员(只要是超级管理员或租户管理中的一个就视为管理员)
isTenantAdminT userId, int userTypeboolean是否是租户管理员
isTenantAdminboolean是否是租户管理员
isLoginboolean检查当前用户是否已登录

示例:

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;
}

权限控制

权限异常码

错误码异常类描述
-12NotPermissionException没有访问权限,请联系管理员授权
-13NotRoleException没有访问权限,请联系管理员授权
-11NotLoginException认证超时,无法访问系统资源
-10NotLoginException认证失败,无法访问系统资源

权限配置

名称类型描述
enabledbooleantrue启用,false 禁用,默认为 false
excludesList排除地址示例:
- /.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) {
           
        }
    }
}