欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 社会 > Spring Boot整合Sa-Token:轻量级权限认证框架实战

Spring Boot整合Sa-Token:轻量级权限认证框架实战

2025/3/17 8:16:34 来源:https://blog.csdn.net/m0_74760466/article/details/146274906  浏览:    关键词:Spring Boot整合Sa-Token:轻量级权限认证框架实战

一、Sa-Token简介

Sa-Token是一款轻量级Java权限认证框架,专为解决Web系统的登录认证、权限控制、Session会话等问题而设计。其核心优势在于API简洁、功能丰富、扩展性强,支持注解鉴权、多账号体系、单点登录等场景相比Shiro和Spring Security,Sa-Token的学习成本和集成难度更低,适合快速开发。

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。

项目官网:https://sa-token.cc/index.html

二、环境准备

使用springboot前确保已经安装JDK,使用这行命令即可查看当前JDK版本。

java -version

如果没有安装JDK请自行到Oracle官网下载安装,推荐JDK版本在17及以上。

下面将sa-token的起步依赖导入到springboot项目中。

		<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-spring-boot3-starter</artifactId><version>1.40.0</version></dependency>

如果不是springboot3.x版本以上的起步依赖导入下面这个

<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-spring-boot-starter</artifactId><version>1.34.0</version>
</dependency>

三、核心功能实现

在application.yml文件中添加如下配置:

spring:application:name: spring-sa-token
server:port: 8081#sa-token configuration
sa-token:token-name: satokenactive-timeout: -1same-token-timeout: 2592000is-concurrent: falseis-share: falsetoken-style: uuidis-log: true

1.登录认证

下面我们进行模拟登录认证的流程,新建一个controller,导入以下代码:

import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** 登录测试*/
@RestController
@RequestMapping("/acc/")
public class LoginController {// 测试登录  ---- http://localhost:8081/acc/doLogin?name=zhang&pwd=123456@RequestMapping("doLogin")public SaResult doLogin(String name, String pwd) {// 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对if("zhang".equals(name) && "123456".equals(pwd)) {StpUtil.login(10001);return SaResult.ok("登录成功");}return SaResult.error("登录失败");}// 查询登录状态  ---- http://localhost:8081/acc/isLogin@RequestMapping("isLogin")public SaResult isLogin() {return SaResult.ok("是否登录:" + StpUtil.isLogin());}// 查询 Token 信息  ---- http://localhost:8081/acc/tokenInfo@RequestMapping("tokenInfo")public SaResult tokenInfo() {return SaResult.data(StpUtil.getTokenInfo());}// 测试注销  ---- http://localhost:8081/acc/logout@RequestMapping("logout")public SaResult logout() {StpUtil.logout();return SaResult.ok();}}

启动springboot工程后在浏览器输入地址测试。

首先测试登录功能

登录成功后查询登录状态:

查询token信息:

测试退出登录:

2.权限认证

权限认证就是查询一下有没有权限,有就通过,没有就禁止访问。

例如:当前账号拥有权限码集合 ["user-add", "user-delete", "user-get"],这时候我来校验权限 "user-update",则其结果就是:验证失败,禁止访问

import cn.dev33.satoken.stp.StpInterface;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.List;/*** 自定义权限加载接口实现类*/
@Component    // 保证此类被 SpringBoot 扫描,完成 Sa-Token 的自定义权限验证扩展
public class StpInterfaceImpl implements StpInterface {/*** 返回一个账号所拥有的权限码集合*/@Overridepublic List<String> getPermissionList(Object loginId, String loginType) {// 本 list 仅做模拟,实际项目中要根据具体业务逻辑来查询权限List<String> list = new ArrayList<String>();list.add("101");list.add("user.add");list.add("user.update");list.add("user.get");// list.add("user.delete");list.add("art.*");return list;}/*** 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验)*/@Overridepublic List<String> getRoleList(Object loginId, String loginType) {// 本 list 仅做模拟,实际项目中要根据具体业务逻辑来查询角色List<String> list = new ArrayList<String>();list.add("admin");list.add("super-admin");return list;}}

在案例中,我们重写了StpInterface接口中的getPermissionList和getRoleList集合,这样权限就加入到标识集合中,用户访问某个接口时会去查询权限集合中的权限,如果用户有此权限就通过,反之就禁止访问。

3.踢人下线

所谓踢人下线,核心操作就是找到指定 loginId 对应的 Token,并设置其失效。

强制注销

StpUtil.logout(10001);                    // 强制指定账号注销下线 
StpUtil.logout(10001, "PC");              // 强制指定账号指定端注销下线 
StpUtil.logoutByTokenValue("token");      // 强制指定 Token 注销下线 

踢人下线

StpUtil.kickout(10001);                    // 将指定账号踢下线 
StpUtil.kickout(10001, "PC");              // 将指定账号指定端踢下线
StpUtil.kickoutByTokenValue("token");      // 将指定 Token 踢下线

强制注销 踢人下线 的区别在于:

强制注销等价于对方主动调用了注销方法,再次访问会提示:Token无效。

踢人下线不会清除Token信息,而是将其打上特定标记,再次访问会提示:Token已被踢下线。

4.注解鉴权

注解鉴权 —— 优雅的将鉴权与业务代码分离!

  • @SaCheckLogin: 登录校验 —— 只有登录之后才能进入该方法。
  • @SaCheckRole("admin"): 角色校验 —— 必须具有指定角色标识才能进入该方法。
  • @SaCheckPermission("user:add"): 权限校验 —— 必须具有指定权限才能进入该方法。
  • @SaCheckSafe: 二级认证校验 —— 必须二级认证之后才能进入该方法。
  • @SaCheckHttpBasic: HttpBasic校验 —— 只有通过 HttpBasic 认证后才能进入该方法。
  • @SaCheckHttpDigest: HttpDigest校验 —— 只有通过 HttpDigest 认证后才能进入该方法。
  • @SaIgnore:忽略校验 —— 表示被修饰的方法或类无需进行注解鉴权和路由拦截器鉴权。
  • @SaCheckDisable("comment"):账号服务封禁校验 —— 校验当前账号指定服务是否被封禁。

Sa-Token 使用全局拦截器完成注解鉴权功能,为了不为项目带来不必要的性能负担,拦截器默认处于关闭状态
因此,为了使用注解鉴权,必须手动将 Sa-Token 的全局拦截器注册到项目中

手动注册拦截器

@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {// 注册 Sa-Token 拦截器,打开注解式鉴权功能 @Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册 Sa-Token 拦截器,打开注解式鉴权功能 registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");    }
}

注册拦截器之后就可以使用注解鉴权

// 登录校验:只有登录之后才能进入该方法 
@SaCheckLogin                        
@RequestMapping("info")
public String info() {return "查询用户信息";
}// 角色校验:必须具有指定角色才能进入该方法 
@SaCheckRole("super-admin")        
@RequestMapping("add")
public String add() {return "用户增加";
}// 权限校验:必须具有指定权限才能进入该方法 
@SaCheckPermission("user-add")        
@RequestMapping("add")
public String add() {return "用户增加";
}// 二级认证校验:必须二级认证之后才能进入该方法 
@SaCheckSafe()        
@RequestMapping("add")
public String add() {return "用户增加";
}// Http Basic 校验:只有通过 Http Basic 认证后才能进入该方法 
@SaCheckHttpBasic(account = "sa:123456")
@RequestMapping("add")
public String add() {return "用户增加";
}// Http Digest 校验:只有通过 Http Digest 认证后才能进入该方法 
@SaCheckHttpDigest(value = "sa:123456")
@RequestMapping("add")
public String add() {return "用户增加";
}// 校验当前账号是否被封禁 comment 服务,如果已被封禁会抛出异常,无法进入方法 
@SaCheckDisable("comment")                
@RequestMapping("send")
public String send() {return "查询用户信息";
}

设定校验模式

@SaCheckRole@SaCheckPermission注解可以进行设置校验模式

// 注解式鉴权:只要具有其中一个权限即可通过校验 
@RequestMapping("atJurOr")
@SaCheckPermission(value = {"user-add", "user-all", "user-delete"}, mode = SaMode.OR)        
public SaResult atJurOr() {return SaResult.data("用户信息");
}

mode有两种取值:

  • SaMode.AND,标注一组权限,会话必须全部具有才可通过校验。
  • SaMode.OR,标注一组权限,会话只要具有其一即可通过校验。

角色权限双重验证

假设有以下业务场景:一个接口在具有权限 user.add 或角色 admin 时可以调通。怎么写?

// 角色权限双重 “or校验”:具备指定权限或者指定角色即可通过校验
@RequestMapping("userAdd")
@SaCheckPermission(value = "user.add", orRole = "admin")        
public SaResult userAdd() {return SaResult.data("用户信息");
}

orRole 字段代表权限校验未通过时的次要选择,两者只要其一校验成功即可进入请求方法,其有三种写法:

  • 写法一:orRole = "admin",代表需要拥有角色 admin 。
  • 写法二:orRole = {"admin", "manager", "staff"},代表具有三个角色其一即可。
  • 写法三:orRole = {"admin, manager, staff"},代表必须同时具有三个角色。

忽略认证

@SaCheckLogin
@RestController
public class TestController {// ... 其它方法 // 此接口加上了 @SaIgnore 可以游客访问 @SaIgnore@RequestMapping("getList")public SaResult getList() {// ... return SaResult.ok(); }
}

5.路由注解鉴权

注册Sa-token路由拦截器

@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {// 注册拦截器@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册 Sa-Token 拦截器,校验规则为 StpUtil.checkLogin() 登录校验。registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin())).addPathPatterns("/**").excludePathPatterns("/user/doLogin"); }
}

以上代码注册了一个基于 StpUtil.checkLogin() 的登录校验拦截器,并且排除了/user/doLogin接口用来开放登录(除了/user/doLogin以外的所有接口都需要登录才能访问)。

校验函数详解

@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {// 注册 Sa-Token 的拦截器@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册路由拦截器,自定义认证规则 registry.addInterceptor(new SaInterceptor(handler -> {// 登录校验 -- 拦截所有路由,并排除/user/doLogin 用于开放登录 SaRouter.match("/**", "/user/doLogin", r -> StpUtil.checkLogin());// 角色校验 -- 拦截以 admin 开头的路由,必须具备 admin 角色或者 super-admin 角色才可以通过认证 SaRouter.match("/admin/**", r -> StpUtil.checkRoleOr("admin", "super-admin"));// 权限校验 -- 不同模块校验不同权限 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"));SaRouter.match("/notice/**", r -> StpUtil.checkPermission("notice"));SaRouter.match("/comment/**", r -> StpUtil.checkPermission("comment"));// 甚至你可以随意的写一个打印语句SaRouter.match("/**", r -> System.out.println("----啦啦啦----"));// 连缀写法SaRouter.match("/**").check(r -> System.out.println("----啦啦啦----"));})).addPathPatterns("/**");}
}

6.自定义token风格

// 1. token-style=uuid    —— uuid风格 (默认风格)
"623368f0-ae5e-4475-a53f-93e4225f16ae"// 2. token-style=simple-uuid    —— 同上,uuid风格, 只不过去掉了中划线
"6fd4221395024b5f87edd34bc3258ee8"// 3. token-style=random-32    —— 随机32位字符串
"qEjyPsEA1Bkc9dr8YP6okFr5umCZNR6W"// 4. token-style=random-64    —— 随机64位字符串
"v4ueNLEpPwMtmOPMBtOOeIQsvP8z9gkMgIVibTUVjkrNrlfra5CGwQkViDjO8jcc"// 5. token-style=random-128    —— 随机128位字符串
"nojYPmcEtrFEaN0Otpssa8I8jpk8FO53UcMZkCP9qyoHaDbKS6dxoRPky9c6QlftQ0pdzxRGXsKZmUSrPeZBOD6kJFfmfgiRyUmYWcj4WU4SSP2ilakWN1HYnIuX0Olj"// 6. token-style=tik    —— tik风格
"gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__"

7.账号封禁

对指定账号进行封禁:

// 封禁指定账号 
StpUtil.disable(10001, 86400); 

参数含义:

  • 参数1:要封禁的账号id。
  • 参数2:封禁时间,单位:秒,此为 86400秒 = 1天(此值为 -1 时,代表永久封禁)。

对于正在登录的账号,将其封禁并不会使它立即掉线,如果我们需要它即刻下线,可采用先踢再封禁的策略

// 先踢下线
StpUtil.kickout(10001); 
// 再封禁账号
StpUtil.disable(10001, 86400); 

待到下次登录时,我们先校验一下这个账号是否已被封禁

// 校验指定账号是否已被封禁,如果被封禁则抛出异常 `DisableServiceException`
StpUtil.checkDisable(10001); // 通过校验后,再进行登录:
StpUtil.login(10001); 

账号封禁模块的所有方法

// 封禁指定账号 
StpUtil.disable(10001, 86400); // 获取指定账号是否已被封禁 (true=已被封禁, false=未被封禁) 
StpUtil.isDisable(10001); // 校验指定账号是否已被封禁,如果被封禁则抛出异常 `DisableServiceException`
StpUtil.checkDisable(10001); // 获取指定账号剩余封禁时间,单位:秒,如果该账号未被封禁,则返回-2 
StpUtil.getDisableTime(10001); // 解除封禁
StpUtil.untieDisable(10001); 

8.密码加密

常见的加密算法

// md5加密 
SaSecureUtil.md5("123456");// sha1加密 
SaSecureUtil.sha1("123456");// sha256加密 
SaSecureUtil.sha256("123456");

对称加密

// 定义秘钥和明文
String key = "123456";
String text = "Sa-Token 一个轻量级java权限认证框架";// 加密 
String ciphertext = SaSecureUtil.aesEncrypt(key, text);
System.out.println("AES加密后:" + ciphertext);// 解密 
String text2 = SaSecureUtil.aesDecrypt(key, ciphertext);
System.out.println("AES解密后:" + text2);

非对称加密

// 定义私钥和公钥 
String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAO+wmt01pwm9lHMdq7A8gkEigk0XKMfjv+4IjAFhWCSiTeP7dtlnceFJbkWxvbc7Qo3fCOpwmfcskwUc3VSgyiJkNJDs9ivPbvlt8IU2bZ+PBDxYxSCJFrgouVOpAr8ar/b6gNuYTi1vt3FkGtSjACFb002/68RKUTye8/tdcVilAgMBAAECgYA1COmrSqTUJeuD8Su9ChZ0HROhxR8T45PjMmbwIz7ilDsR1+E7R4VOKPZKW4Kz2VvnklMhtJqMs4MwXWunvxAaUFzQTTg2Fu/WU8Y9ha14OaWZABfChMZlpkmpJW9arKmI22ZuxCEsFGxghTiJQ3tK8npj5IZq5vk+6mFHQ6aJAQJBAPghz91Dpuj+0bOUfOUmzi22obWCBncAD/0CqCLnJlpfOoa9bOcXSusGuSPuKy5KiGyblHMgKI6bq7gcM2DWrGUCQQD3SkOcmia2s/6i7DUEzMKaB0bkkX4Ela/xrfV+A3GzTPv9bIBamu0VIHznuiZbeNeyw7sVo4/GTItq/zn2QJdBAkEA8xHsVoyXTVeShaDIWJKTFyT5dJ1TR++/udKIcuiNIap34tZdgGPI+EM1yoTduBM7YWlnGwA9urW0mj7F9e9WIQJAFjxqSfmeg40512KP/ed/lCQVXtYqU7U2BfBTg8pBfhLtEcOg4wTNTroGITwe2NjL5HovJ2n2sqkNXEio6Ji0QQJAFLW1Kt80qypMqot+mHhS+0KfdOpaKeMWMSR4Ij5VfE63WzETEeWAMQESxzhavN1WOTb3/p6icgcVbgPQBaWhGg==";
String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDvsJrdNacJvZRzHauwPIJBIoJNFyjH47/uCIwBYVgkok3j+3bZZ3HhSW5Fsb23O0KN3wjqcJn3LJMFHN1UoMoiZDSQ7PYrz275bfCFNm2fjwQ8WMUgiRa4KLlTqQK/Gq/2+oDbmE4tb7dxZBrUowAhW9NNv+vESlE8nvP7XXFYpQIDAQAB";// 文本
String text = "Sa-Token 一个轻量级java权限认证框架";// 使用公钥加密
String ciphertext = SaSecureUtil.rsaEncryptByPublic(publicKey, text);
System.out.println("公钥加密后:" + ciphertext);// 使用私钥解密
String text2 = SaSecureUtil.rsaDecryptByPrivate(privateKey, ciphertext);
System.out.println("私钥解密后:" + text2); 

生成公、私钥

// 生成一对公钥和私钥,其中Map对象 (private=私钥, public=公钥)
System.out.println(SaSecureUtil.rsaGenerateKeyPair());

Base64编码与解码

// 文本
String text = "Sa-Token 一个轻量级java权限认证框架";// 使用Base64编码
String base64Text = SaBase64Util.encode(text);
System.out.println("Base64编码后:" + base64Text);// 使用Base64解码
String text2 = SaBase64Util.decode(base64Text);
System.out.println("Base64解码后:" + text2); 

Bcrypt加密

// 使用方法
String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt()); // 使用checkpw方法检查被加密的字符串是否与原始字符串匹配:
BCrypt.checkpw(candidate_password, stored_hash); // gensalt方法提供了可选参数 (log_rounds) 来定义加盐多少,也决定了加密的复杂度:
String strong_salt = BCrypt.gensalt(10);
String stronger_salt = BCrypt.gensalt(12); 

9.全局监听器

Sa-Token 提供一种侦听器机制,通过注册侦听器,你可以订阅框架的一些关键性事件,例如:用户登录、退出、被踢下线等。

自定义监听器实现

/*** 自定义侦听器的实现 */
@Component
public class MySaTokenListener implements SaTokenListener {/** 每次登录时触发 */@Overridepublic void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {System.out.println("---------- 自定义侦听器实现 doLogin");}/** 每次注销时触发 */@Overridepublic void doLogout(String loginType, Object loginId, String tokenValue) {System.out.println("---------- 自定义侦听器实现 doLogout");}/** 每次被踢下线时触发 */@Overridepublic void doKickout(String loginType, Object loginId, String tokenValue) {System.out.println("---------- 自定义侦听器实现 doKickout");}/** 每次被顶下线时触发 */@Overridepublic void doReplaced(String loginType, Object loginId, String tokenValue) {System.out.println("---------- 自定义侦听器实现 doReplaced");}/** 每次被封禁时触发 */@Overridepublic void doDisable(String loginType, Object loginId, String service, int level, long disableTime) {System.out.println("---------- 自定义侦听器实现 doDisable");}/** 每次被解封时触发 */@Overridepublic void doUntieDisable(String loginType, Object loginId, String service) {System.out.println("---------- 自定义侦听器实现 doUntieDisable");}/** 每次二级认证时触发 */@Overridepublic void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {System.out.println("---------- 自定义侦听器实现 doOpenSafe");}/** 每次退出二级认证时触发 */@Overridepublic void doCloseSafe(String loginType, String tokenValue, String service) {System.out.println("---------- 自定义侦听器实现 doCloseSafe");}/** 每次创建Session时触发 */@Overridepublic void doCreateSession(String id) {System.out.println("---------- 自定义侦听器实现 doCreateSession");}/** 每次注销Session时触发 */@Overridepublic void doLogoutSession(String id) {System.out.println("---------- 自定义侦听器实现 doLogoutSession");}/** 每次Token续期时触发 */@Overridepublic void doRenewTimeout(String tokenValue, Object loginId, long timeout) {System.out.println("---------- 自定义侦听器实现 doRenewTimeout");}
}

10.全局过滤器

拦截器与过滤器区别:

  1. 相比于拦截器,过滤器更加底层,执行时机更靠前,有利于防渗透扫描。
  2. 过滤器可以拦截静态资源,方便我们做一些权限控制。
  3. 部分Web框架根本就没有提供拦截器功能,但几乎所有的Web框架都会提供过滤器机制。

但是过滤器也有一些缺点,比如:

  1. 由于太过底层,导致无法率先拿到HandlerMethod对象,无法据此添加一些额外功能。
  2. 由于拦截的太全面了,导致我们需要对很多特殊路由(如/favicon.ico)做一些额外处理。
  3. 在Spring中,过滤器中抛出的异常无法进入全局@ExceptionHandler,我们必须额外编写代码进行异常处理。

Sa-Token同时提供过滤器和拦截器机制,不是为了让谁替代谁,而是为了让大家根据自己的实际业务合理选择,拥有更多的发挥空间。

/*** [Sa-Token 权限认证] 配置类 */
@Configuration
public class SaTokenConfigure {/*** 注册 [Sa-Token全局过滤器] */@Beanpublic SaServletFilter getSaServletFilter() {return new SaServletFilter()// 指定 拦截路由 与 放行路由.addInclude("/**").addExclude("/favicon.ico")    /* 排除掉 /favicon.ico */// 认证函数: 每次请求执行 .setAuth(obj -> {System.out.println("---------- 进入Sa-Token全局认证 -----------");// 登录认证 -- 拦截所有路由,并排除/user/doLogin 用于开放登录 SaRouter.match("/**", "/user/doLogin", () -> StpUtil.checkLogin());// 更多拦截处理方式,请参考“路由拦截式鉴权”章节 */})// 异常处理函数:每次认证函数发生异常时执行此函数 .setError(e -> {System.out.println("---------- 进入Sa-Token异常处理 -----------");return SaResult.error(e.getMessage());})// 前置函数:在每次认证函数之前执行(BeforeAuth 不受 includeList 与 excludeList 的限制,所有请求都会进入).setBeforeAuth(r -> {// ---------- 设置一些安全响应头 ----------SaHolder.getResponse()// 服务器名称 .setServer("sa-server")// 是否可以在iframe显示视图: DENY=不可以 | SAMEORIGIN=同域下可以 | ALLOW-FROM uri=指定域名下可以 .setHeader("X-Frame-Options", "SAMEORIGIN")// 是否启用浏览器默认XSS防护: 0=禁用 | 1=启用 | 1; mode=block 启用, 并在检查到XSS攻击时,停止渲染页面 .setHeader("X-XSS-Protection", "1; mode=block")// 禁用浏览器内容嗅探 .setHeader("X-Content-Type-Options", "nosniff");});}}

四、常见问题

  1. Token失效场景:默认30天无操作失效,可通过activity-timeout调整 
  2. 多端登录控制:设置is-concurrent: false禁止并发登录 
  3. 跨域问题:需在拦截器中配置CORS或使用@CrossOrigin注解 

五、总结

Sa-Token优势

  • 开箱即用,API简洁
  • 支持注解鉴权与RBAC模型
  • 无缝整合Spring生态
  • 提供JWT、单点登录等扩展方案

适用场景:中小型Web应用、微服务架构、快速开发项目

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词