Compare commits

..

16 Commits

Author SHA1 Message Date
click33
6a96d2fabd refactor: 重构模块依赖层级 2026-02-27 12:14:01 +08:00
click33
f40f0d3adf refactor: sa-token-dependencies 重构为 sa-token-basic-dependencies 2026-02-27 10:04:44 +08:00
click33
6398ef4ca6 refactor: 重构 springboot reactor 相关集成包,优化依赖关系 2026-02-27 06:18:06 +08:00
click33
e8fe3a83c7 demo: 新增 sa-token-demo-webflux-springboot4 示例 2026-02-27 03:47:12 +08:00
click33
8c0177c598 feat: 新增 sa-token-reactor-spring-boot4-starter 集成包 2026-02-27 03:45:54 +08:00
click33
fc27e2a49d refactor: 移除 sa-token-spring-boot-autoconfig 模块 2026-02-27 03:22:14 +08:00
click33
7c95ec51fa refactor: 重构 springboot 相关集成包,优化依赖关系 2026-02-27 00:25:12 +08:00
click33
9b7efbbcc8 demo: 新增 SpringBoot4 整合 demo 示例 2026-02-26 01:22:32 +08:00
click33
2d671dc783 feat: 新增 sa-token-spring-boot4-starter 集成包 2026-02-26 01:19:41 +08:00
click33
f6c5131ad2 test: 新增 sa-token-jackson3 单元测试 2026-02-25 19:55:09 +08:00
click33
9c07f7a007 feat: 添加 sa-token-jackson3 插件 2026-02-25 19:13:18 +08:00
click33
c2becdf498 docs: 公众号文章列表同步 2026-02-23 16:42:26 +08:00
click33
defb2a0052 docs: 优化 readme 2026-02-20 19:29:15 +08:00
click33
007bf2aa47 docs: 优化 readme. 2026-02-20 19:19:31 +08:00
click33
7725322002 docs: 优化案例库展示 2026-02-20 19:00:24 +08:00
click33
91f97089b5 chore: 增加忽略 .vscode 目录 2026-02-20 19:00:03 +08:00
134 changed files with 2883 additions and 1287 deletions

1
.gitignore vendored
View File

@@ -12,6 +12,7 @@ unpackage/
/.factorypath
.idea/
.vscode/
sa-token-three-plugin/
sa-token-doc/big-file/

View File

@@ -2,7 +2,7 @@
<img alt="logo" src="https://sa-token.cc/logo.png" width="150" height="150">
</p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Sa-Token v1.44.0</h1>
<h4 align="center">开源、免费、一站式 java 权限认证框架,让鉴权变得简单、优雅! </h4>
<h4 align="center">开源、免费、一站式 java 权限认证框架,让鉴权变得简单、优雅! </h4>
<p align="center">
<a href="https://gitee.com/dromara/sa-token/stargazers"><img src="https://gitee.com/dromara/sa-token/badge/star.svg?theme=gvp"></a>
<a href="https://gitee.com/dromara/sa-token/members"><img src="https://gitee.com/dromara/sa-token/badge/fork.svg?theme=gvp"></a>
@@ -218,7 +218,7 @@ Sa-Token 采用 Apache-2.0 开源协议,承诺框架本身与在线文档永
### 🚀 开源集成案例
### 🚀 优秀开源集成案例
- [[ Snowy ]](https://gitee.com/xiaonuobase/snowy):国内首个国密前后分离快速开发平台,采用 Vue3 + Vite + SpringBoot + Mp + HuTool + SaToken。
- [[ RuoYi-Vue-Plus ]](https://gitee.com/dromara/RuoYi-Vue-Plus)重写RuoYi-Vue所有功能 集成 Sa-Token、Mybatis-Plus、Xxl-Job、knife4j、OSS 定期同步。

View File

@@ -24,6 +24,7 @@ cd sa-token-demo-solon & call mvn clean & cd ..
cd sa-token-demo-solon-redisson & call mvn clean & cd ..
cd sa-token-demo-springboot & call mvn clean & cd ..
cd sa-token-demo-springboot3-redis & call mvn clean & cd ..
cd sa-token-demo-springboot4-redis & call mvn clean & cd ..
cd sa-token-demo-springboot-low-version & call mvn clean & cd ..
cd sa-token-demo-springboot-redis & call mvn clean & cd ..
cd sa-token-demo-springboot-redisson & call mvn clean & cd ..

View File

@@ -19,6 +19,7 @@
<!-- 所有模块 -->
<modules>
<module>sa-token-dependencies</module>
<module>sa-token-special-dependencies</module>
<module>sa-token-bom</module>
<module>sa-token-core</module>
<module>sa-token-starter</module>
@@ -75,7 +76,11 @@
<dependencyManagement>
<dependencies>
<!--
导入 sa-token-dependencies 所有版本定义,并传导到每个子项目。
需要注意的是:该 import 只会导入 <dependencyManagement> 部分,而不会导入 <dependencies> 部分和 <properties> 部分。
-->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dependencies</artifactId>

View File

@@ -51,6 +51,11 @@
<artifactId>sa-token-reactor-spring-boot3-starter</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-reactor-spring-boot4-starter</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-servlet</artifactId>
@@ -68,7 +73,17 @@
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-autoconfig</artifactId>
<artifactId>sa-token-spring-boot-webmvc-reactor-v2v3v4-common</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-reactor-v2v3v4-common</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-webmvc-v3v4-common</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
@@ -81,6 +96,11 @@
<artifactId>sa-token-spring-boot3-starter</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot4-starter</artifactId>
<version>${revision}</version>
</dependency>
<!-- endregion-->
<!-- region sa-token-plugin -->
@@ -119,6 +139,11 @@
<artifactId>sa-token-jackson</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jackson3</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-fastjson</artifactId>

View File

@@ -16,7 +16,7 @@
<description>A Java Web lightweight authority authentication framework, comprehensive function, easy to use</description>
<dependencies>
<!-- Zero dependence -->
<!-- Zero Dependence -->
</dependencies>

View File

@@ -35,6 +35,7 @@
<module>sa-token-demo-solon-redisson</module>
<module>sa-token-demo-springboot</module>
<module>sa-token-demo-springboot3-redis</module>
<module>sa-token-demo-springboot4-redis</module>
<module>sa-token-demo-springboot-low-version</module>
<module>sa-token-demo-springboot-redis</module>
<module>sa-token-demo-springboot-redisson</module>
@@ -55,6 +56,7 @@
<module>sa-token-demo-thymeleaf</module>
<module>sa-token-demo-webflux</module>
<module>sa-token-demo-webflux-springboot3</module>
<module>sa-token-demo-webflux-springboot4</module>
<module>sa-token-demo-websocket</module>
<module>sa-token-demo-websocket-spring</module>
<module>sa-token-demo-loveqq-boot</module>

View File

@@ -10,7 +10,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.1</version>
<version>3.5.11</version>
<relativePath/>
</parent>
@@ -48,7 +48,7 @@
<!-- Sa-Token整合 Redis (使用jackson序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-jackson</artifactId>
<artifactId>sa-token-redis-template</artifactId>
<version>${sa-token.version}</version>
</dependency>

View File

@@ -0,0 +1,65 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-demo-springboot4-redis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- SpringBoot 4 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.3</version>
<relativePath/>
</parent>
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
</properties>
<dependencies>
<!-- SpringBoot 4 依赖webmvc 替代 deprecated 的 starter-webaspectj 替代 starter-aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aspectj</artifactId>
</dependency>
<!-- Sa-Token 权限认证, 在线文档https://sa-token.cc/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot4-starter</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- Sa-Token整合 Redis -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-template</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- 提供Redis连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- @ConfigurationProperties -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,20 @@
package com.pj;
import cn.dev33.satoken.SaManager;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Sa-Token 整合 SpringBoot4 示例,整合 redis
* @author click33
*
*/
@SpringBootApplication
public class SaTokenSpringBoot4Application {
public static void main(String[] args) {
SpringApplication.run(SaTokenSpringBoot4Application.class, args);
System.out.println("\n🎉 启动成功Sa-Token配置如下" + SaManager.getConfig());
}
}

View File

@@ -0,0 +1,56 @@
package com.pj.current;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.pj.util.AjaxJson;
import cn.dev33.satoken.exception.DisableServiceException;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* 全局异常处理
*/
@RestControllerAdvice
public class GlobalException {
// 全局异常拦截(拦截项目中的所有异常)
@ExceptionHandler
public AjaxJson handlerException(Exception e, HttpServletRequest request, HttpServletResponse response)
throws Exception {
// 打印堆栈,以供调试
System.out.println("全局异常---------------");
e.printStackTrace();
// 不同异常返回不同状态码
AjaxJson aj = null;
if (e instanceof NotLoginException) { // 如果是未登录异常
NotLoginException ee = (NotLoginException) e;
aj = AjaxJson.getNotLogin().setMsg(ee.getMessage());
}
else if(e instanceof NotRoleException) { // 如果是角色异常
NotRoleException ee = (NotRoleException) e;
aj = AjaxJson.getNotJur("无此角色:" + ee.getRole());
}
else if(e instanceof NotPermissionException) { // 如果是权限异常
NotPermissionException ee = (NotPermissionException) e;
aj = AjaxJson.getNotJur("无此权限:" + ee.getPermission());
}
else if(e instanceof DisableServiceException) { // 如果是被封禁异常
DisableServiceException ee = (DisableServiceException) e;
aj = AjaxJson.getNotJur("当前账号 " + ee.getService() + " 服务已被封禁 (level=" + ee.getLevel() + ")" + ee.getDisableTime() + "秒后解封");
}
else { // 普通异常, 输出500 + 异常信息
aj = AjaxJson.getError(e.getMessage());
}
// 返回给前端
return aj;
}
}

View File

@@ -0,0 +1,26 @@
//package com.pj.current;
//
//import java.io.IOException;
//
//import org.springframework.boot.web.servlet.error.ErrorController;
//import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.RestController;
//
//import cn.dev33.satoken.util.SaResult;
//import jakarta.servlet.http.HttpServletRequest;
//import jakarta.servlet.http.HttpServletResponse;
//
///**
// * 处理 404
// * @author click33
// */
//@RestController
//public class NotFoundHandle implements ErrorController {
//
// @RequestMapping("/error")
// public Object error(HttpServletRequest request, HttpServletResponse response) throws IOException {
// response.setStatus(200);
// return SaResult.get(404, "not found", null);
// }
//
//}

View File

@@ -0,0 +1,71 @@
package com.pj.satoken;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.util.SaResult;
/**
* [Sa-Token 权限认证] 配置类
* @author click33
*
*/
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
/**
* 注册 Sa-Token 拦截器打开注解鉴权功能
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器打开注解鉴权功能
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
}
/**
* 注册 [Sa-Token 全局过滤器]
*/
@Bean
public SaServletFilter getSaServletFilter() {
return new SaServletFilter()
// 指定 [拦截路由] 与 [放行路由]
.addInclude("/**")// .addExclude("/favicon.ico")
// 认证函数: 每次请求执行
.setAuth(obj -> {
// System.out.println("---------- sa全局认证 " + SaHolder.getRequest().getRequestPath());
})
// 异常处理函数:每次认证函数发生异常时执行此函数
.setError(e -> {
System.out.println("---------- sa全局异常 ");
e.printStackTrace();
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")
;
})
;
}
}

View File

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

View File

@@ -0,0 +1,80 @@
package com.pj.test;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.dev33.satoken.annotation.SaCheckHttpBasic;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaCheckRole;
import cn.dev33.satoken.annotation.SaCheckSafe;
import cn.dev33.satoken.annotation.SaMode;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
/**
* 注解鉴权测试
* @author click33
*
*/
@RestController
@RequestMapping("/at/")
public class AtController {
// 登录认证,登录之后才可以进入方法 ---- http://localhost:8082/at/checkLogin
@SaCheckLogin
@RequestMapping("checkLogin")
public SaResult checkLogin() {
return SaResult.ok();
}
// 权限认证具备user-add权限才可以进入方法 ---- http://localhost:8082/at/checkPermission
@SaCheckPermission("user-add")
@RequestMapping("checkPermission")
public SaResult checkPermission() {
return SaResult.ok();
}
// 权限认证,同时具备所有权限才可以进入 ---- http://localhost:8082/at/checkPermissionAnd
@SaCheckPermission({"user-add", "user-delete", "user-update"})
@RequestMapping("checkPermissionAnd")
public SaResult checkPermissionAnd() {
return SaResult.ok();
}
// 权限认证,只要具备其中一个就可以进入 ---- http://localhost:8082/at/checkPermissionOr
@SaCheckPermission(value = {"user-add", "user-delete", "user-update"}, mode = SaMode.OR)
@RequestMapping("checkPermissionOr")
public SaResult checkPermissionOr() {
return SaResult.ok();
}
// 角色认证只有具备admin角色才可以进入 ---- http://localhost:8082/at/checkRole
@SaCheckRole("admin")
@RequestMapping("checkRole")
public SaResult checkRole() {
return SaResult.ok();
}
// 完成二级认证 ---- http://localhost:8082/at/openSafe
@RequestMapping("openSafe")
public SaResult openSafe() {
StpUtil.openSafe(200); // 打开二级认证有效期为200秒
return SaResult.ok();
}
// 通过二级认证后才可以进入 ---- http://localhost:8082/at/checkSafe
@SaCheckSafe
@RequestMapping("checkSafe")
public SaResult checkSafe() {
return SaResult.ok();
}
// 通过Basic认证后才可以进入 ---- http://localhost:8082/at/checkBasic
@SaCheckHttpBasic(account = "sa:123456")
@RequestMapping("checkBasic")
public SaResult checkBasic() {
return SaResult.ok();
}
}

View File

@@ -0,0 +1,48 @@
package com.pj.test;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
/**
* 登录测试
* @author click33
*
*/
@RestController
@RequestMapping("/acc/")
public class LoginController {
// 测试登录 ---- http://localhost:8082/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:8082/acc/isLogin
@RequestMapping("isLogin")
public SaResult isLogin() {
return SaResult.ok("是否登录:" + StpUtil.isLogin());
}
// 查询 Token 信息 ---- http://localhost:8082/acc/tokenInfo
@RequestMapping("tokenInfo")
public SaResult tokenInfo() {
return SaResult.data(StpUtil.getTokenInfo());
}
// 测试注销 ---- http://localhost:8082/acc/logout
@RequestMapping("logout")
public SaResult logout() {
StpUtil.logout();
return SaResult.ok();
}
}

View File

@@ -0,0 +1,61 @@
package com.pj.test;
import java.util.ArrayList;
import java.util.List;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.pj.util.Ttime;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
/**
* 压力测试
* @author click33
*
*/
@RestController
@RequestMapping("/s-test/")
public class StressTestController {
// 测试 浏览器访问: http://localhost:8082/s-test/login
// 测试前,请先将 is-read-cookie 配置为 false
@RequestMapping("login")
public SaResult login() {
// StpUtil.getTokenSession().logout();
// StpUtil.logoutByLoginId(10001);
int count = 10; // 循环多少轮
int loginCount = 10000; // 每轮循环多少次
// 循环10次 取平均时间
List<Double> list = new ArrayList<>();
for (int i = 1; i <= count; i++) {
System.out.println("\n---------------------第" + i + "轮---------------------");
Ttime t = new Ttime().start();
// 每次登录的次数
for (int j = 1; j <= loginCount; j++) {
StpUtil.login("1000" + j, "PC-" + j);
if(j % 1000 == 0) {
System.out.println("已登录:" + j);
}
}
t.end();
list.add((t.returnMs() + 0.0) / 1000);
System.out.println("" + i + "" + "用时:" + t.toString());
}
// System.out.println(((SaTokenDaoDefaultImpl)SaTokenManager.getSaTokenDao()).dataMap.size());
System.out.println("\n---------------------测试结果---------------------");
System.out.println(list.size() + "次测试: " + list);
double ss = 0;
for (int i = 0; i < list.size(); i++) {
ss += list.get(i);
}
System.out.println("平均用时: " + ss / list.size());
return SaResult.ok();
}
}

View File

@@ -0,0 +1,43 @@
package com.pj.test;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.servlet.util.SaTokenContextJakartaServletUtil;
import cn.dev33.satoken.spring.SpringMVCUtil;
import cn.dev33.satoken.util.SaResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 测试专用Controller
* @author click33
*
*/
@RestController
@RequestMapping("/test/")
public class TestController {
// 测试 浏览器访问: http://localhost:8082/test/test
@RequestMapping("test")
public SaResult test() {
System.out.println("------------进来了");
System.out.println(SpringMVCUtil.getRequest());
System.out.println(SaTokenContextJakartaServletUtil.getRequest());
return SaResult.ok();
}
// 测试 浏览器访问: http://localhost:8082/test/test2
@RequestMapping("test2")
public SaResult test2() {
return SaResult.ok();
}
// 测试 浏览器访问: http://localhost:8082/test/getRequestPath
@RequestMapping("getRequestPath")
public SaResult getRequestPath() {
System.out.println("-------------- 测试请求 path 获取");
System.out.println("request.getRequestURI() " + SpringMVCUtil.getRequest().getRequestURI());
System.out.println("saRequest.getRequestPath() " + SaHolder.getRequest().getRequestPath());
return SaResult.ok();
}
}

View File

@@ -0,0 +1,162 @@
package com.pj.util;
import java.io.Serializable;
import java.util.List;
/**
* ajax请求返回Json格式数据的封装
*/
public class AjaxJson implements Serializable{
private static final long serialVersionUID = 1L; // 序列化版本号
public static final int CODE_SUCCESS = 200; // 成功状态码
public static final int CODE_ERROR = 500; // 错误状态码
public static final int CODE_WARNING = 501; // 警告状态码
public static final int CODE_NOT_JUR = 403; // 无权限状态码
public static final int CODE_NOT_LOGIN = 401; // 未登录状态码
public static final int CODE_INVALID_REQUEST = 400; // 无效请求状态码
public int code; // 状态码
public String msg; // 描述信息
public Object data; // 携带对象
public Long dataCount; // 数据总数,用于分页
/**
* 返回code
* @return
*/
public int getCode() {
return this.code;
}
/**
* 给msg赋值连缀风格
*/
public AjaxJson setMsg(String msg) {
this.msg = msg;
return this;
}
public String getMsg() {
return this.msg;
}
/**
* 给data赋值连缀风格
*/
public AjaxJson setData(Object data) {
this.data = data;
return this;
}
/**
* 将data还原为指定类型并返回
*/
@SuppressWarnings("unchecked")
public <T> T getData(Class<T> cs) {
return (T) data;
}
// ============================ 构建 ==================================
public AjaxJson(int code, String msg, Object data, Long dataCount) {
this.code = code;
this.msg = msg;
this.data = data;
this.dataCount = dataCount;
}
// 返回成功
public static AjaxJson getSuccess() {
return new AjaxJson(CODE_SUCCESS, "ok", null, null);
}
public static AjaxJson getSuccess(String msg) {
return new AjaxJson(CODE_SUCCESS, msg, null, null);
}
public static AjaxJson getSuccess(String msg, Object data) {
return new AjaxJson(CODE_SUCCESS, msg, data, null);
}
public static AjaxJson getSuccessData(Object data) {
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
}
public static AjaxJson getSuccessArray(Object... data) {
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
}
// 返回失败
public static AjaxJson getError() {
return new AjaxJson(CODE_ERROR, "error", null, null);
}
public static AjaxJson getError(String msg) {
return new AjaxJson(CODE_ERROR, msg, null, null);
}
// 返回警告
public static AjaxJson getWarning() {
return new AjaxJson(CODE_ERROR, "warning", null, null);
}
public static AjaxJson getWarning(String msg) {
return new AjaxJson(CODE_WARNING, msg, null, null);
}
// 返回未登录
public static AjaxJson getNotLogin() {
return new AjaxJson(CODE_NOT_LOGIN, "未登录,请登录后再次访问", null, null);
}
// 返回没有权限的
public static AjaxJson getNotJur(String msg) {
return new AjaxJson(CODE_NOT_JUR, msg, null, null);
}
// 返回一个自定义状态码的
public static AjaxJson get(int code, String msg){
return new AjaxJson(code, msg, null, null);
}
// 返回分页和数据的
public static AjaxJson getPageData(Long dataCount, Object data){
return new AjaxJson(CODE_SUCCESS, "ok", data, dataCount);
}
// 返回,根据受影响行数的(大于0=ok小于0=error)
public static AjaxJson getByLine(int line){
if(line > 0){
return getSuccess("ok", line);
}
return getError("error").setData(line);
}
// 返回,根据布尔值来确定最终结果的 (true=okfalse=error)
public static AjaxJson getByBoolean(boolean b){
return b ? getSuccess("ok") : getError("error");
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@SuppressWarnings("rawtypes")
@Override
public String toString() {
String data_string = null;
if(data == null){
} else if(data instanceof List){
data_string = "List(length=" + ((List)data).size() + ")";
} else {
data_string = data.toString();
}
return "{"
+ "\"code\": " + this.getCode()
+ ", \"msg\": \"" + this.getMsg() + "\""
+ ", \"data\": " + data_string
+ ", \"dataCount\": " + dataCount
+ "}";
}
}

View File

@@ -0,0 +1,63 @@
package com.pj.util;
/**
* 用于测试用时
* @author click33
*
*/
public class Ttime {
private long start=0; //开始时间
private long end=0; //结束时间
public static Ttime t = new Ttime(); //static快捷使用
/**
* 开始计时
* @return
*/
public Ttime start() {
start=System.currentTimeMillis();
return this;
}
/**
* 结束计时
*/
public Ttime end() {
end=System.currentTimeMillis();
return this;
}
/**
* 返回所用毫秒数
*/
public long returnMs() {
return end-start;
}
/**
* 格式化输出结果
*/
public void outTime() {
System.out.println(this.toString());
}
/**
* 结束并格式化输出结果
*/
public void endOutTime() {
this.end().outTime();
}
@Override
public String toString() {
return (returnMs() + 0.0) / 1000 + "s"; // 格式化为0.01s
}
}

View File

@@ -0,0 +1,50 @@
# 端口
server:
port: 8082
# sa-token 配置
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
spring:
data:
# 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

View File

@@ -53,7 +53,7 @@
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

View File

@@ -105,6 +105,17 @@ public class TestController {
return SaResult.ok("登录人:" + StpUtil.getLoginIdByToken(satoken));
}
// API测试SaSession 写值 --- http://localhost:8081/test/sessionSet
@RequestMapping("sessionSet")
public Mono<SaResult> sessionSet() {
return SaReactorHolder.sync(() -> {
System.out.println("session name 值为:" + StpUtil.getSession().get("name"));
StpUtil.getSession().set("name", "zhangsan");
System.out.println("session name 值为:" + StpUtil.getSession().get("name"));
return SaResult.data(StpUtil.getSession().get("name"));
});
}
// 测试 浏览器访问: http://localhost:8081/test/test
@RequestMapping("test")
public SaResult test() {

View File

@@ -20,27 +20,27 @@ sa-token:
is-log: true
spring:
# redis配置
redis:
# Redis数据库索引默认为0
database: 0
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码默认为空
password:
# 连接超时时间(毫秒)
timeout: 10000ms
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
data:
# redis配置
redis:
# Redis数据库索引默认为0
database: 0
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码默认为空
password:
# 连接超时时间(毫秒)
timeout: 10000ms
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0

View File

@@ -0,0 +1,61 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-demo-webflux-springboot4</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- SpringBoot 4 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.3</version>
<relativePath/>
</parent>
<!-- 定义 Sa-Token 版本号 -->
<properties>
<sa-token.version>1.44.0</sa-token.version>
</properties>
<dependencies>
<!-- springboot依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- Sa-Token 权限认证Reactor响应式集成, 在线文档https://sa-token.cc/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-reactor-spring-boot4-starter</artifactId>
<version>${sa-token.version}</version>
</dependency>
<!-- Sa-Token 整合 Redis -->
<!-- <dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-template</artifactId>
<version>${sa-token.version}</version>
</dependency> -->
<!-- 提供redis连接池 -->
<!-- <dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency> -->
<!-- @ConfigurationProperties -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,22 @@
package com.pj;
import cn.dev33.satoken.SaManager;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Sa-Token整合webflux 示例 (springboot4)
*
* @author click33
* @since 2026年2月27日
*
*/
@SpringBootApplication
public class SaTokenWebfluxSpringboot4Application {
public static void main(String[] args) {
SpringApplication.run(SaTokenWebfluxSpringboot4Application.class, args);
System.out.println("\n启动成功Sa-Token配置如下" + SaManager.getConfig());
}
}

View File

@@ -13,42 +13,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.reactor.filter;
package com.pj.satoken;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaTokenContextModelBox;
import cn.dev33.satoken.exception.BackResultException;
import cn.dev33.satoken.exception.StopMatchException;
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
import cn.dev33.satoken.reactor.util.SaReactorOperateUtil;
import cn.dev33.satoken.strategy.SaStrategy;
import cn.dev33.satoken.util.SaTokenConsts;
import org.springframework.core.annotation.Order;
import cn.dev33.satoken.stp.StpUtil;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
/**
* CORS 跨域策略过滤器 (基于 Reactor)
*
* @author click33
* @since 1.42.0
* 自定义过滤器
*/
@Order(SaTokenConsts.CORS_FILTER_ORDER)
public class SaTokenCorsFilterForReactor implements WebFilter {
@Component
public class MyFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
System.out.println("进入自定义过滤器");
try {
// set 上下文再调用 Sa-Token 同步 API并在 finally 里清除上下文
SaReactorSyncHolder.setContext(exchange);
SaTokenContextModelBox box = SaHolder.getContext().getModelBox();
SaStrategy.instance.corsHandle.execute(box.getRequest(), box.getResponse(), box.getStorage());
}
catch (StopMatchException ignored) {}
catch (BackResultException e) {
return SaReactorOperateUtil.writeResult(exchange, e.getMessage());
System.out.println(StpUtil.isLogin());
}
finally {
SaReactorSyncHolder.clearContext();

View File

@@ -0,0 +1,39 @@
package com.pj.satoken;
import cn.dev33.satoken.reactor.filter.SaReactorFilter;
import cn.dev33.satoken.util.SaResult;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* [Sa-Token 权限认证] 配置类
* @author click33
*
*/
@Configuration
public class SaTokenConfigure {
/**
* 注册 [sa-token全局过滤器]
*/
@Bean
public SaReactorFilter getSaReactorFilter() {
return new SaReactorFilter()
// 指定 [拦截路由]
.addInclude("/**")
// 指定 [放行路由]
.addExclude("/favicon.ico")
// 指定[认证函数]: 每次请求执行
.setAuth(r -> {
System.out.println("---------- sa全局认证");
// SaRouter.match("/test/test", () -> StpUtil.checkLogin());
})
// 指定[异常处理函数]:每次[认证函数]发生异常时执行此函数
.setError(e -> {
System.out.println("---------- sa全局异常 ");
return SaResult.error(e.getMessage());
})
;
}
}

View File

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

View File

@@ -0,0 +1,32 @@
package com.pj.test;
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
@Configuration
public class DefineRoutes {
/**
* 函数式编程,初始化路由表
* @return 路由表
*/
@Bean
public RouterFunction<ServerResponse> getRoutes() {
return RouterFunctions.route(RequestPredicates.GET("/fun"), req -> {
return SaReactorSyncHolder.setContext(req.exchange(), () -> {
System.out.println("是否登录:" + StpUtil.isLogin());
SaResult res = SaResult.data(StpUtil.getTokenInfo());
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).bodyValue(res);
});
});
}
}

View File

@@ -0,0 +1,19 @@
package com.pj.test;
import cn.dev33.satoken.util.SaResult;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理
*/
@RestControllerAdvice
public class GlobalException {
@ExceptionHandler
public SaResult handlerException(Exception e) {
e.printStackTrace();
return SaResult.error(e.getMessage());
}
}

View File

@@ -0,0 +1,126 @@
package com.pj.test;
import cn.dev33.satoken.reactor.context.SaReactorHolder;
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.time.Duration;
/**
* 测试专用Controller
* @author click33
*
*/
@RestController
@RequestMapping("/test/")
public class TestController {
@Autowired
UserService userService;
// 登录测试Controller 里调用 Sa-Token API --- http://localhost:8081/test/login
@RequestMapping("login")
public Mono<SaResult> login(@RequestParam(defaultValue="10001") String id) {
return SaReactorHolder.sync(() -> {
StpUtil.login(id);
return SaResult.ok("登录成功");
});
}
// API测试手动设置上下文、try-finally 形式 --- http://localhost:8081/test/isLogin
@RequestMapping("isLogin")
public SaResult isLogin(ServerWebExchange exchange) {
try {
SaReactorSyncHolder.setContext(exchange);
System.out.println("是否登录:" + StpUtil.isLogin());
return SaResult.data(StpUtil.getTokenInfo());
} finally {
SaReactorSyncHolder.clearContext();
}
}
// API测试手动设置上下文、lambda 表达式形式 --- http://localhost:8081/test/isLogin2
@RequestMapping("isLogin2")
public SaResult isLogin2(ServerWebExchange exchange) {
SaResult res = SaReactorSyncHolder.setContext(exchange, ()->{
System.out.println("是否登录:" + StpUtil.isLogin());
return SaResult.data(StpUtil.getTokenInfo());
});
return SaResult.data(res);
}
// API测试自动设置上下文、lambda 表达式形式 --- http://localhost:8081/test/isLogin3
@RequestMapping("isLogin3")
public Mono<SaResult> isLogin3() {
return SaReactorHolder.sync(() -> {
System.out.println("是否登录:" + StpUtil.isLogin());
userService.isLogin();
return SaResult.data(StpUtil.getTokenInfo());
});
}
// API测试自动设置上下文、调用 userService Mono 方法 --- http://localhost:8081/test/isLogin4
@RequestMapping("isLogin4")
public Mono<SaResult> isLogin4() {
return userService.findUserIdByNamePwd("ZhangSan", "123456").flatMap(userId -> {
return SaReactorHolder.sync(() -> {
StpUtil.login(userId);
return SaResult.data(StpUtil.getTokenInfo());
});
});
}
// API测试切换线程、复杂嵌套调用 --- http://localhost:8081/test/isLogin5
@RequestMapping("isLogin5")
public Mono<SaResult> isLogin5() {
System.out.println("线程id-----" + Thread.currentThread().getId());
// 要点:在流里调用 Sa-Token API 之前,必须用 SaReactorHolder.sync( () -> {} ) 进行包裹
return Mono.delay(Duration.ofSeconds(1))
.doOnNext(r-> System.out.println("线程id-----" + Thread.currentThread().getId()))
.map(r-> SaReactorHolder.sync( () -> userService.isLogin() ))
.map(r-> userService.findUserIdByNamePwd("ZhangSan", "123456"))
.map(r-> SaReactorHolder.sync( () -> userService.isLogin() ))
.flatMap(isLogin -> {
System.out.println("是否登录 " + isLogin);
return SaReactorHolder.sync(() -> {
System.out.println("是否登录 " + StpUtil.isLogin());
return SaResult.data(StpUtil.getTokenInfo());
});
});
}
// API测试使用上下文无关的API --- http://localhost:8081/test/isLogin6
@RequestMapping("isLogin6")
public SaResult isLogin6(@CookieValue("satoken") String satoken) {
System.out.println("token 为:" + satoken);
System.out.println("登录人:" + StpUtil.getLoginIdByToken(satoken));
return SaResult.ok("登录人:" + StpUtil.getLoginIdByToken(satoken));
}
// API测试SaSession 写值 --- http://localhost:8081/test/sessionSet
@RequestMapping("sessionSet")
public Mono<SaResult> sessionSet() {
return SaReactorHolder.sync(() -> {
System.out.println("session name 值为:" + StpUtil.getSession().get("name"));
StpUtil.getSession().set("name", "zhangsan");
System.out.println("session name 值为:" + StpUtil.getSession().get("name"));
return SaResult.data(StpUtil.getSession().get("name"));
});
}
// 测试 浏览器访问: http://localhost:8081/test/test
@RequestMapping("test")
public SaResult test() {
System.out.println("线程id------- " + Thread.currentThread().getId());
return SaResult.ok();
}
}

View File

@@ -0,0 +1,25 @@
package com.pj.test;
import cn.dev33.satoken.stp.StpUtil;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
/**
* 模拟 Service 方法
* @author click33
* @since 2025/4/6
*/
@Service
public class UserService {
public boolean isLogin() {
System.out.println("UserService 里调用 API 测试,是否登录:" + StpUtil.isLogin());
return StpUtil.isLogin();
}
public Mono<Long> findUserIdByNamePwd(String name, String pwd) {
// ...
return Mono.just(10001L);
}
}

View File

@@ -0,0 +1,47 @@
# 端口
server:
port: 8081
# sa-token 配置
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
spring:
data:
# redis配置
redis:
# Redis数据库索引默认为0
database: 0
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码默认为空
password:
# 连接超时时间(毫秒)
timeout: 10000ms
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0

View File

@@ -53,7 +53,7 @@
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

View File

@@ -105,6 +105,17 @@ public class TestController {
return SaResult.ok("登录人:" + StpUtil.getLoginIdByToken(satoken));
}
// API测试SaSession 写值 --- http://localhost:8081/test/sessionSet
@RequestMapping("sessionSet")
public Mono<SaResult> sessionSet() {
return SaReactorHolder.sync(() -> {
System.out.println("session name 值为:" + StpUtil.getSession().get("name"));
StpUtil.getSession().set("name", "zhangsan");
System.out.println("session name 值为:" + StpUtil.getSession().get("name"));
return SaResult.data(StpUtil.getSession().get("name"));
});
}
// 测试 浏览器访问: http://localhost:8081/test/test
@RequestMapping("test")
public SaResult test() {

View File

@@ -14,13 +14,10 @@
<properties>
<revision>1.44.0</revision>
<!-- 统一定义依赖版本号 -->
<springboot.version>2.7.18</springboot.version>
<springboot3.version>3.4.3</springboot3.version>
<spring-web.low.version>5.3.39</spring-web.low.version>
<reactor-core.version>3.7.4</reactor-core.version>
<!-- 第三方依赖版本号 -->
<jackson-databind.version>2.13.4.1</jackson-databind.version>
<jackson-datatype-jsr310.version>2.11.2</jackson-datatype-jsr310.version>
<jackson3-databind.version>3.1.0</jackson3-databind.version>
<servlet-api.version>3.1.0</servlet-api.version>
<jakarta-servlet-api.version>6.0.0</jakarta-servlet-api.version>
<thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
@@ -63,40 +60,19 @@
<version>${jakarta-servlet-api.version}</version>
</dependency>
<!-- spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${springboot.version}</version>
</dependency>
<!-- spring-boot-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${springboot.version}</version>
</dependency>
<!-- spring-web -->
<!-- <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.7</version>
</dependency> -->
<!-- reactor-core -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>${reactor-core.version}</version>
</dependency>
<!-- jackson-databind -->
<!-- jackson2 databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-databind.version}</version>
</dependency>
<!-- jackson3 databind (tools.jackson) -->
<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson3-databind.version}</version>
</dependency>
<!-- solon -->
<dependency>
@@ -147,25 +123,7 @@
<version>${loveqq.version}</version>
</dependency>
<!-- test -->
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${springboot.version}</version>
</dependency> -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${springboot.version}</version>
</dependency>
<!-- ****************** sa-token-plugin 相关依赖 ****************** -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>${springboot.version}</version>
</dependency>
<!-- Redisson 相关操作API -->
<dependency>
@@ -236,26 +194,6 @@
<version>${freemarker.version}</version>
</dependency>
<!-- spring-boot-starter-thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>${springboot.version}</version>
</dependency>
<!-- spring-boot-starter-aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>${springboot.version}</version>
</dependency>
<!-- spring-boot-starter-actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>${springboot.version}</version>
</dependency>
<!-- hutool-jwt -->
<dependency>
@@ -324,7 +262,7 @@
</dependencies>
</dependencyManagement>
<!-- 父仓库 -->
<!-- 父仓库:不能使用 sa-token-parent因其 dependencyManagement 会 import 本模块,形成循环依赖 -->
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
@@ -332,7 +270,7 @@
<relativePath/>
</parent>
<!-- 项目构建 -->
<!-- 项目构建:因无法继承 sa-token-parent此处需单独配置 -->
<build>
<plugins>
<plugin>
@@ -345,7 +283,7 @@
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- 统一版本号管理 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
@@ -372,7 +310,7 @@
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>

View File

@@ -2,7 +2,7 @@
<img alt="logo" src="https://sa-token.cc/logo.png" width="150" height="150">
</p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Sa-Token v1.44.0</h1>
<h5 align="center">开源、免费、一站式 java 权限认证框架,让鉴权变得简单、优雅!</h5>
<h5 align="center">开源、免费、一站式 java 权限认证框架,让鉴权变得简单、优雅!</h5>
<p align="center" class="badge-box">
<a href="https://gitee.com/dromara/sa-token/stargazers"><img src="https://gitee.com/dromara/sa-token/badge/star.svg?theme=gvp"></a>
<a href="https://gitee.com/dromara/sa-token/members"><img src="https://gitee.com/dromara/sa-token/badge/fork.svg?theme=gvp"></a>
@@ -16,14 +16,14 @@
---
## 前言:️️
## 📝 前言:️️
为了保证新同学不迷路,请允许我唠叨一下:无论您从何处看到本篇文章,最新开发文档永远在:[https://sa-token.cc](https://sa-token.cc)
建议收藏在浏览器书签,如果您已经身处本网站下,则请忽略此条说明。
本文档将会尽力讲解每个功能的设计原因、应用场景,用心阅读文档,你学习到的将不止是 `Sa-Token` 框架本身,更是绝大多数场景下权限设计的最佳实践。
## Sa-Token 介绍
## 🛠️ Sa-Token 介绍
**Sa-Token** 是一个轻量级 Java 权限认证框架,主要解决:**登录认证**、**权限认证**、**单点登录**、**OAuth2.0**、**分布式Session会话**、**微服务网关鉴权**
等一系列权限相关问题。
@@ -85,7 +85,7 @@ registry.addInterceptor(new SaInterceptor(handler -> {
当你受够 Shiro、SpringSecurity 等框架的三拜九叩之后你就会明白相对于这些传统老牌框架Sa-Token 的 API 设计是多么的简单、优雅!
## Sa-Token 功能一览
## 🎉 Sa-Token 功能一览
Sa-Token 目前主要五大功能模块登录认证、权限认证、单点登录、OAuth2.0、微服务鉴权。
@@ -132,7 +132,7 @@ Sa-Token 目前主要五大功能模块:登录认证、权限认证、单点
<img class="s-w" src="/big-file/index/intro/sa-token-js4.png" />
## 疑问解答
## 📖❓ 疑问解答
**1、Sa-Token 功能全不全?**
@@ -167,7 +167,7 @@ Sa-Token 采用 Apache-2.0 开源协议,承诺框架本身与在线文档永
## 开源仓库 Star 趋势
## 📈 开源仓库 Star 趋势
<p class="un-dec-a-pre"></p>
@@ -179,12 +179,11 @@ Sa-Token 采用 Apache-2.0 开源协议,承诺框架本身与在线文档永
[GitHub](https://github.com/dromara/sa-token)
## 使用Sa-Token的开源项目
## 🚀 使用 Sa-Token 的开源项目
参考:[Sa-Token 生态](/more/link)
## 交流群
## 💬 交流群
加入 Sa-Token 框架 QQ、微信讨论群[点击加入](/more/join-group.md)

View File

@@ -205,7 +205,7 @@
</div>
<!-- help 按钮 -->
<div class="help-btn">❤️ 技术求助</div>
<!-- <div class="help-btn">❤️ 技术求助</div> -->
<!-- ew-wa -->
<div class="ew-wa">
<p>
@@ -260,7 +260,7 @@
<!-- <script src="./static/is-fill-in-wj-plugin.js?v=7"></script> -->
<link rel="stylesheet" href="./static/custom-docsify-plugins/doc-lock-plugin.css">
<!-- <script src="./static/custom-docsify-plugins/doc-lock-plugin.js"></script> -->
<script src="./static/custom-docsify-plugins/doc-lock-by-gzh-plugin.js"></script>
<!-- <script src="./static/custom-docsify-plugins/doc-lock-by-gzh-plugin.js"></script> -->
<script>
var saTokenTopVersion = '1.44.0'; // Sa-Token最新版本
var name = '<img style="width: 60px; height: 60px; vertical-align: middle;" src="logo.png" alt="logo" /> ';
@@ -322,7 +322,7 @@
myDocsifyPlugin, // 基础插件
// window.isStarPlugin, // 是否 star
// window.isFillInWjPlugin // 问卷填写
window.docLockPlugin, // 章节锁
// window.docLockPlugin, // 章节锁
],
}
</script>

View File

@@ -5,6 +5,26 @@
---
- [[ 公众号 ] SpringBoot3 + Sa-Token 单点登录30分钟上手代码可复制新手零踩坑](https://mp.weixin.qq.com/s/2jN9HotfYttLFMzHE54XiA) 2026-2-21
- [[ 公众号 ] Sa-Token Session会话三种模型彻底搞懂不再傻傻分不清](https://mp.weixin.qq.com/s/gpqogF0QyahuqJIqhs6DUg) 2026-2-21
- [[ 公众号 ] SpringBoot3 + Sa-Token 双Token登录认证实战避坑版](https://mp.weixin.qq.com/s/LDSCSZYuUIkQA91MYXhnGQ) 2026-2-20
- [[ CSDN ] SaToken实战5分钟搞定微信小程序登录功能附完整代码](https://blog.csdn.net/weixin_29218509/article/details/158226233) 2026-2-20
- [[ 公众号 ] 告别SpringSecuritySa-Token+Gateway+Nacos极简鉴权实战](https://mp.weixin.qq.com/s/lFcH7XyLRtaNH6q4-TzHbQ) 2026-2-19
- [[ 公众号 ] SpringSecurity、Shiro和Sa-Token哪个更好](https://mp.weixin.qq.com/s/BEvk1ohFntL7iorDEvqIpg) 2026-2-18
- [[ CSDN ] Sa-Token 1.42.0实战5分钟搞定API Key权限隔离与TOTP双因子认证](https://blog.csdn.net/weixin_29271053/article/details/158175327) 2026-2-18
- [[ CSDN ] SaToken权限注解全解析@SaCheckPermission和@SaCheckRole的20种实战用法](https://blog.csdn.net/weixin_28454475/article/details/158163447) 2026-2-18
- [[ 公众号 ] 5 分钟上手 Sa-TokenSpring Boot 权限认证从未如此简单](https://mp.weixin.qq.com/s/2iRMPQdfBEgqSgeld3crXw) 2026-2-17
- [[ 公众号 ] SpringSecurity、Shiro和Sa-Token哪个更好](https://mp.weixin.qq.com/s/QdR1tyIXN8GvWbhN48XTjA) 2026-2-13
- [[ 公众号 ] sa-token前后端分离集成redis与jwt基础案例](https://mp.weixin.qq.com/s/c1UYdxuRjWudGnpQa_A72w) 2026-2-11
- [[ 公众号 ] Sa-Token(一)之简介及入门告别鉴权内耗让每一位Java开发者都能轻松上手](https://mp.weixin.qq.com/s/JLQSMAgqK1U0vtrdtRsKlA) 2026-2-11

View File

@@ -1,12 +1,15 @@
# 使用 Sa-Token 的开源项目
> 集成 Sa-Token 的开源案例收集,取自 [Awesome-Sa-Token](https://gitee.com/sa-tokens/awesome-sa-token),定期同步
> 集成 Sa-Token 的开源案例收集,取自 Awesome-Sa-Token定期同步
> [Gitee](https://gitee.com/sa-tokens/awesome-sa-token)、
> [GitHub](https://github.com/sa-tokens/awesome-sa-token)、
> [AtomGit](https://atomgit.com/sa-tokens/awesome-sa-token)
---
### 后台管理
### 📊 后台管理
- [[ art-design-pro-java ]](https://github.com/anganing/art-design-pro-java)SpringBoot17+Sa-token+Art-Design-Pro+Unibest 技术栈的企业级后台开发管理系统。
@@ -100,7 +103,7 @@
### 微服务相关
### 🚀 微服务相关
- [[ XHan Admin ]](https://gitee.com/sun-xiaohan/xh-admin-frontend)XHan Admin 是一个开源免费(前后端分离)中后台管理系统基础解决方案, 无专业版收费,所有功能毫无保留的贡献给开源社区,使用最新技术栈全新开发,无任何历史代码包袱。
@@ -133,7 +136,7 @@
- [[ LangChat ]](https://github.com/TyCoding/langchat)( OpenAI / Gemini / Ollama / Azure / 智谱 / 阿里通义大模型 / 百度千帆大模型), Java生态下AI大模型产品解决方案快速构建企业级AI知识库、AI机器人应用
### 商城
### 🛒 商城
- [[ litemall-plus ]](https://gitee.com/ysling-org/litemall-plus)微信小程序SaaS商城系统可支持多小程序同时运行。
@@ -142,7 +145,7 @@
- [[ Huanxing-mall ]](https://gitee.com/lijiaxing_boy/huanxing-mall)HuanXing 商城基于SpringCloud 2021 & Alibaba + Sa-token前端基于 Vue3 +Element plus 的微服务商城
### 博客
### 📝 博客
- [[ jthink ]](https://gitee.com/wtsoftware/jthink) 一个基于 SpringBoot + Sa-Token + Thymeleaf 的博客系统
@@ -156,24 +159,24 @@
### 插件
### 🔌 插件
- [[ Sa-Token-Plugin ]](https://gitee.com/bootx/sa-token-plugin)Sa-Token第三方插件实现基于Sa-Token-Core提供一些与官方不同实现机制的的插件集合作为Sa-Token开源生态的补充
- [[ quarkus-sa-token ]](https://github.com/quarkiverse/quarkus-sa-token) quarkus 整合 Sa-Token。
### 多语言
### 🌐 多语言
- Rust[[ sa-token-rust ]](https://github.com/llc-993/sa-token-rust) 一个轻量级、高性能的 Rust 认证授权框架。
- Rust[[ sa-token-rust ]](https://github.com/sa-tokens/sa-token-rust) 一个轻量级、高性能的 Rust 认证授权框架。
- Go[[ sa-token-go ]](https://github.com/click33/sa-token-go) 一个轻量级、高性能的 Go 权限认证框架。
- Go[[ sa-token-go ]](https://github.com/sa-tokens/sa-token-go) 一个轻量级、高性能的 Go 权限认证框架。
- PHP[[ real-token ]](https://gitee.com/jinan-jimeng-network_0/real-token) 一个轻量级 thinkphp6 权限认证框架,让鉴权变得简单、优雅!
### 其它
### 📦 其它
- [[ Glowxq-OJ ]](https://github.com/glowxq/glowxq-oj)Glowxq-OJ 专业开源在线编程测评系统 | 基于Spring Boot 3.x + Java 21 + Vue 3构建 | 支持ACM/ICPC竞赛、信奥赛训练、编程教育 | 多语言判题、实时竞赛、在线IDE | Docker一键部署 | Modern Online Judge Platform for Competitive Programming & Coding Education。
@@ -201,4 +204,5 @@
- [[ Dinky ]](https://github.com/DataLinkDC/dinky)基于Apache Flink的实时数据开发平台实现敏捷的数据开发、部署和运维
- [[ mldong ]](https://gitee.com/mldong/mldong)SpringBoot + Vue3 快速开发平台、自研工作流引擎
- [[ mldong ]](https://gitee.com/mldong/mldong)SpringBoot + Vue3 快速开发平台、自研工作流引擎

View File

@@ -20,6 +20,7 @@
<modules>
<!-- 通用插件 -->
<module>sa-token-jackson</module>
<module>sa-token-jackson3</module>
<module>sa-token-fastjson</module>
<module>sa-token-fastjson2</module>
<module>sa-token-snack3</module>
@@ -53,12 +54,19 @@
<module>sa-token-redisson-spring-boot-starter</module>
</modules>
<dependencies>
<!-- spring-boot-configuration -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- 默认使用 sa-token springboot2 相关依赖版本定义,子模块也可以引入其它的进行覆盖 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot2-dependencies</artifactId>
<version>${revision}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@@ -20,7 +20,7 @@ import cn.dev33.satoken.json.SaJsonTemplateDefaultImpl;
import cn.dev33.satoken.json.SaJsonTemplateForJackson;
/**
* SaToken 插件安装JSON 转换器 (jackjson 版)
* SaToken 插件安装JSON 转换器 (jackson 版)
*
* @author click33
* @since 1.41.0

View File

@@ -0,0 +1,46 @@
<?xml version='1.0' encoding='utf-8'?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-plugin</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<packaging>jar</packaging>
<name>sa-token-jackson3</name>
<artifactId>sa-token-jackson3</artifactId>
<description>sa-token-jackson3: Sa-Token 与 Jackson 3 整合</description>
<dependencies>
<!-- sa-token-core -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-core</artifactId>
</dependency>
<!-- jackson-databind 3.x (Java 8 时间支持已内置) -->
<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,121 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.json;
import cn.dev33.satoken.exception.SaJsonConvertException;
import cn.dev33.satoken.util.SaFoxUtil;
import tools.jackson.core.JacksonException;
import tools.jackson.databind.DeserializationFeature;
import tools.jackson.databind.DefaultTyping;
import tools.jackson.databind.SerializationFeature;
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import tools.jackson.databind.jsontype.PolymorphicTypeValidator;
import tools.jackson.databind.json.JsonMapper;
import java.util.Map;
/**
* JSON 转换器Jackson 3 版实现
*
* @author click33
* @since 1.45.0
*/
public class SaJsonTemplateForJackson3 implements SaJsonTemplate {
/**
* 底层 Mapper 对象(带多态类型信息,用于 Session 等复杂对象序列化)
*/
public final JsonMapper objectMapper;
/**
* 处理 Map 的 Mapper无多态类型配置用于简单 JSON 解析)
*/
public final JsonMapper mapObjectMapper;
public SaJsonTemplateForJackson3() {
// 1、构建反序列化限制器PolymorphicTypeValidator限制哪些类型可以被自动多态反序列化
// 这里允许所有 Object 类型和其子类型参与多态反序列化,从而支持复杂对象(如 SaSession的序列化、反序列化
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
.allowIfSubType(Object.class) // 允许 Object.class 的子类型被反序列化
.allowIfBaseType(Object.class) // 允许 Object.class 作为基类型(在启用多态时)
.build(); // 构建 Validator 实例
// 2、通过 JsonMapper 的 builder 模式创建 objectMapper用于带多态类型信息的 JSON 处理Jackson3 默认不可变需用构建器)
this.objectMapper = JsonMapper.builder()
// 启用默认多态类型处理,并以 "@class" 作为写入类型信息的属性名(即持久化时包含类型字段),仅对非 final 类型生效
.activateDefaultTypingAsProperty(ptv, DefaultTyping.NON_FINAL, "@class")
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) // 序列化时如果 bean 没有属性,则不抛出异常
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) // 反序列化时如果有未知属性,也不抛出异常(兼容性更好)
.build(); // 构建真正的 JsonMapper 实例
// 3、创建 mapObjectMapper用于简单类型如 Map的序列化和反序列化和 objectMapper 独立,且不启用多态类型
this.mapObjectMapper = JsonMapper.builder()
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) // 序列化时如果 bean 没有属性,则不抛出异常
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) // 反序列化时如果有未知属性,也不抛出异常,避免出错
.build(); // 构建用于处理简单场景的 JsonMapper 实例
}
/**
* 序列化:对象 -> json 字符串
*/
@Override
public String objectToJson(Object obj) {
if (SaFoxUtil.isEmpty(obj)) {
return null;
}
try {
if (obj instanceof Map) {
return mapObjectMapper.writeValueAsString(obj);
}
return objectMapper.writeValueAsString(obj);
} catch (JacksonException e) {
throw new SaJsonConvertException(e);
}
}
/**
* 反序列化json 字符串 → 对象
*/
@Override
public <T> T jsonToObject(String jsonStr, Class<T> type) {
if (SaFoxUtil.isEmpty(jsonStr)) {
return null;
}
try {
return objectMapper.readValue(jsonStr, type);
} catch (JacksonException e) {
throw new SaJsonConvertException(e);
}
}
/**
* 将 json 字符串解析为 Map
*/
@Override
public Map<String, Object> jsonToMap(String jsonStr) {
if (SaFoxUtil.isEmpty(jsonStr)) {
return null;
}
try {
@SuppressWarnings("unchecked")
Map<String, Object> map = mapObjectMapper.readValue(jsonStr, Map.class);
return map;
} catch (JacksonException e) {
throw new SaJsonConvertException(e);
}
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.plugin;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.json.SaJsonTemplateDefaultImpl;
import cn.dev33.satoken.json.SaJsonTemplateForJackson3;
/**
* SaToken 插件安装JSON 转换器 (Jackson 3 版)
*
* @author click33
* @since 1.45.0
*/
public class SaTokenPluginForJackson3 implements SaTokenPlugin {
@Override
public void install() {
// 只有在未提供自定义的 json 解析器时才会生效,给予其较弱的优先级
if (SaManager.getSaJsonTemplate().getClass() == SaJsonTemplateDefaultImpl.class) {
SaManager.setSaJsonTemplate(new SaJsonTemplateForJackson3());
}
}
}

View File

@@ -0,0 +1 @@
cn.dev33.satoken.plugin.SaTokenPluginForJackson3

View File

@@ -32,6 +32,7 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.5.14</version>
<optional>true</optional>
</dependency>
</dependencies>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-parent</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<packaging>pom</packaging>
<artifactId>sa-token-special-dependencies</artifactId>
<name>sa-token-special-dependencies</name>
<description>Sa-Token Special Dependencies - 聚合 Spring Boot 各版本依赖</description>
<modules>
<module>sa-token-spring-boot2-dependencies</module>
<module>sa-token-spring-boot3-dependencies</module>
<module>sa-token-spring-boot4-dependencies</module>
</modules>
</project>

View File

@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- 父仓库 -->
<parent>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-special-dependencies</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<packaging>pom</packaging>
<artifactId>sa-token-spring-boot2-dependencies</artifactId>
<name>sa-token-spring-boot2-dependencies</name>
<description>Sa-Token SpringBoot2 Dependencies</description>
<properties>
<springboot2.version>2.7.18</springboot2.version>
<springboot2-spring.version>5.3.39</springboot2-spring.version>
<reactor-core.version>3.7.4</reactor-core.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- spring-boot-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${springboot2.version}</version>
</dependency>
<!-- spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${springboot2.version}</version>
</dependency>
<!-- spring-boot-configuration-processor -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${springboot2.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springboot2-spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springboot2-spring.version}</version>
</dependency>
<!-- reactor-core -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>${reactor-core.version}</version>
</dependency>
<!-- ****************** sa-token-plugin 相关依赖 ****************** -->
<!-- spring-boot-starter-data-redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>${springboot2.version}</version>
</dependency>
<!-- spring-boot-starter-thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>${springboot2.version}</version>
</dependency>
<!-- spring-boot-starter-aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>${springboot2.version}</version>
</dependency>
<!-- spring-boot-starter-actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>${springboot2.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- 父仓库 -->
<parent>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-special-dependencies</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<packaging>pom</packaging>
<artifactId>sa-token-spring-boot3-dependencies</artifactId>
<name>sa-token-spring-boot3-dependencies</name>
<description>Sa-Token SpringBoot3 Dependencies</description>
<properties>
<springboot3.version>3.5.11</springboot3.version>
<springboot3-spring.version>6.2.16</springboot3-spring.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- spring-boot-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${springboot3.version}</version>
</dependency>
<!-- spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${springboot3.version}</version>
</dependency>
<!-- spring-web (for reactor/webflux) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springboot3-spring.version}</version>
</dependency>
<!-- config (optional) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${springboot3.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- 父仓库 -->
<parent>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-special-dependencies</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<packaging>pom</packaging>
<artifactId>sa-token-spring-boot4-dependencies</artifactId>
<name>sa-token-spring-boot4-dependencies</name>
<description>Sa-Token SpringBoot4 Dependencies</description>
<properties>
<springboot4.version>4.0.3</springboot4.version>
<springboot4-spring.version>7.0.3</springboot4-spring.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- spring-boot-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${springboot4.version}</version>
</dependency>
<!-- spring-boot-starter-webmvc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
<version>${springboot4.version}</version>
</dependency>
<!-- spring-web (for reactor/webflux) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springboot4-spring.version}</version>
</dependency>
<!-- spring-boot-starter-webflux (for reactor) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>${springboot4.version}</version>
</dependency>
<!-- config (optional) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${springboot4.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@@ -20,11 +20,15 @@
<modules>
<module>sa-token-servlet</module>
<module>sa-token-jakarta-servlet</module>
<module>sa-token-spring-boot-autoconfig</module>
<module>sa-token-spring-boot-webmvc-reactor-v2v3v4-common</module>
<module>sa-token-spring-boot-reactor-v2v3v4-common</module>
<module>sa-token-spring-boot-starter</module>
<module>sa-token-spring-boot-webmvc-v3v4-common</module>
<module>sa-token-spring-boot3-starter</module>
<module>sa-token-spring-boot4-starter</module>
<module>sa-token-reactor-spring-boot-starter</module>
<module>sa-token-reactor-spring-boot3-starter</module>
<module>sa-token-reactor-spring-boot4-starter</module>
<module>sa-token-solon-plugin</module>
<module>sa-token-jboot-plugin</module>
<module>sa-token-jfinal-plugin</module>

View File

@@ -24,50 +24,32 @@
<optional>true</optional>
</dependency>
<!-- spring-web (optional) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<optional>true</optional>
</dependency>
<!-- reactor-core -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<!-- jackson-databind (optional) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<optional>true</optional>
</dependency>
<!-- config -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- sa-token-spring-boot-autoconfig -->
<!-- sa-token-spring-boot-reactor-v2v3v4-common -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-autoconfig</artifactId>
<artifactId>sa-token-spring-boot-reactor-v2v3v4-common</artifactId>
</dependency>
<!-- sa-token-jackson: JSON serialization -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jackson</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.39</version>
</dependency>
<!-- sa-token springboot2 相关依赖版本定义 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot2-dependencies</artifactId>
<version>${revision}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

View File

@@ -1,98 +0,0 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.reactor.context;
import cn.dev33.satoken.fun.SaRetGenericFunction;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import reactor.util.context.Context;
import reactor.util.context.ContextView;
/**
* Reactor 上下文操作(异步),持有当前请求的 ServerWebExchange 全局引用
*
* @author click33
* @since 1.19.0
*/
public class SaReactorHolder {
/**
* ServerWebExchange key
*/
public static final String EXCHANGE_KEY = "SA_REACTOR_EXCHANGE_KEY";
/**
* WebFilterChain key
*/
public static final String CHAIN_KEY = "SA_REACTOR__CHAIN_KEY";
/**
* 在流式上下文写入 ServerWebExchange
* @param ctx 必填
* @param exchange 必填
* @param chain 非必填
* @return /
*/
public static Context setContext(Context ctx, ServerWebExchange exchange, WebFilterChain chain) {
return ctx
.put(EXCHANGE_KEY, exchange)
.put(CHAIN_KEY, chain);
}
/**
* 在流式上下文获取 ServerWebExchange
* @param ctx /
* @return /
*/
public static ServerWebExchange getExchange(ContextView ctx) {
return ctx.get(EXCHANGE_KEY);
}
/**
* 在流式上下文获取 WebFilterChain
* @param ctx /
* @return /
*/
public static WebFilterChain getChain(ContextView ctx) {
return ctx.get(CHAIN_KEY);
}
/**
* 获取 Mono < ServerWebExchange >
* @return /
*/
public static Mono<ServerWebExchange> getMonoExchange() {
return Mono.deferContextual(ctx -> Mono.just(getExchange(ctx)));
}
/**
* 将 exchange 写入到同步上下文中,并执行一段代码,执行完毕清除上下文
*
* @return /
*/
public static <R> Mono<R> sync(SaRetGenericFunction<R> fun) {
return Mono.deferContextual(ctx -> {
try {
SaReactorSyncHolder.setContext(ctx.get(EXCHANGE_KEY));
return Mono.just(fun.run());
} finally {
SaReactorSyncHolder.clearContext();
}
});
}
}

View File

@@ -1,78 +0,0 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.reactor.context;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.context.model.SaResponse;
import cn.dev33.satoken.context.model.SaStorage;
import cn.dev33.satoken.context.model.SaTokenContextModelBox;
import cn.dev33.satoken.fun.SaRetGenericFunction;
import cn.dev33.satoken.reactor.model.SaRequestForReactor;
import cn.dev33.satoken.reactor.model.SaResponseForReactor;
import cn.dev33.satoken.reactor.model.SaStorageForReactor;
import org.springframework.web.server.ServerWebExchange;
/**
* Reactor上下文操作同步持有当前请求的 ServerWebExchange 全局引用
*
* @author click33
* @since 1.19.0
*/
public class SaReactorSyncHolder {
/**
* 在同步上下文写入 ServerWebExchange
* @param exchange /
*/
public static void setContext(ServerWebExchange exchange) {
SaRequest request = new SaRequestForReactor(exchange.getRequest());
SaResponse response = new SaResponseForReactor(exchange.getResponse());
SaStorage storage = new SaStorageForReactor(exchange);
SaManager.getSaTokenContext().setContext(request, response, storage);
}
/**
* 在同步上下文清除 ServerWebExchange
*/
public static void clearContext() {
SaManager.getSaTokenContext().clearContext();
}
/**
* 在同步上下文获取 ServerWebExchange
* @return /
*/
public static ServerWebExchange getExchange() {
SaTokenContextModelBox box = SaManager.getSaTokenContext().getModelBox();
return (ServerWebExchange)box.getStorage().getSource();
}
/**
* 将 exchange 写入到同步上下文中,并执行一段代码,执行完毕清除上下文
* @param exchange /
* @param fun /
*/
public static <R>R setContext(ServerWebExchange exchange, SaRetGenericFunction<R> fun) {
try {
setContext(exchange);
return fun.run();
} finally {
clearContext();
}
}
}

View File

@@ -1,158 +0,0 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.reactor.filter;
import cn.dev33.satoken.exception.BackResultException;
import cn.dev33.satoken.exception.SaTokenException;
import cn.dev33.satoken.exception.StopMatchException;
import cn.dev33.satoken.filter.SaFilter;
import cn.dev33.satoken.filter.SaFilterAuthStrategy;
import cn.dev33.satoken.filter.SaFilterErrorStrategy;
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
import cn.dev33.satoken.reactor.util.SaReactorOperateUtil;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.util.SaTokenConsts;
import org.springframework.core.annotation.Order;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 全局鉴权过滤器 (基于 Reactor)
* <p>
* 默认优先级为 -100尽量保证在其它过滤器之前执行
* </p>
*
* @author click33
* @since 1.19.0
*/
@Order(SaTokenConsts.ASSEMBLY_ORDER)
public class SaReactorFilter implements SaFilter, WebFilter {
/*
* 三种 Filter
* WebFilter Spring WebFlux 的过滤器,用于拦截 Web 请求
* GlobalFilter Spring Cloud Gateway 的全局过滤器,用于拦截 Gateway 请求
* GatewayFilter Spring Cloud Gateway 的局部过滤器,用于拦截 Gateway 请求
*/
// ------------------------ 设置此过滤器 拦截 & 放行 的路由
/**
* 拦截路由
*/
public List<String> includeList = new ArrayList<>();
/**
* 放行路由
*/
public List<String> excludeList = new ArrayList<>();
@Override
public SaReactorFilter addInclude(String... paths) {
includeList.addAll(Arrays.asList(paths));
return this;
}
@Override
public SaReactorFilter addExclude(String... paths) {
excludeList.addAll(Arrays.asList(paths));
return this;
}
@Override
public SaReactorFilter setIncludeList(List<String> pathList) {
includeList = pathList;
return this;
}
@Override
public SaReactorFilter setExcludeList(List<String> pathList) {
excludeList = pathList;
return this;
}
// ------------------------ 钩子函数
/**
* 认证函数:每次请求执行
*/
public SaFilterAuthStrategy auth = r -> {};
/**
* 异常处理函数:每次[认证函数]发生异常时执行此函数
*/
public SaFilterErrorStrategy error = e -> {
throw new SaTokenException(e);
};
/**
* 前置函数:在每次[认证函数]之前执行
* <b>注意点:前置认证函数将不受 includeList 与 excludeList 的限制,所有路由的请求都会进入 beforeAuth</b>
*/
public SaFilterAuthStrategy beforeAuth = r -> {};
@Override
public SaReactorFilter setAuth(SaFilterAuthStrategy auth) {
this.auth = auth;
return this;
}
@Override
public SaReactorFilter setError(SaFilterErrorStrategy error) {
this.error = error;
return this;
}
@Override
public SaReactorFilter setBeforeAuth(SaFilterAuthStrategy beforeAuth) {
this.beforeAuth = beforeAuth;
return this;
}
// ------------------------ filter
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
// ---------- 全局认证处理
try {
SaReactorSyncHolder.setContext(exchange);
beforeAuth.run(null);
SaRouter.match(includeList).notMatch(excludeList).check(r -> auth.run(null));
}
catch (StopMatchException ignored) {}
catch (BackResultException e) {
return SaReactorOperateUtil.writeResult(exchange, e.getMessage());
}
catch (Throwable e) {
return SaReactorOperateUtil.writeResult(exchange, String.valueOf(error.run(e)));
}
finally {
SaReactorSyncHolder.clearContext();
}
return chain.filter(exchange);
}
}

View File

@@ -1,59 +0,0 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.reactor.filter;
import cn.dev33.satoken.reactor.context.SaReactorHolder;
import cn.dev33.satoken.util.SaTokenConsts;
import org.springframework.core.annotation.Order;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
/**
* SaTokenContext 上下文初始化过滤器 (基于 Reactor)
*
* @author click33
* @since 1.42.0
*/
@Order(SaTokenConsts.SA_TOKEN_CONTEXT_FILTER_ORDER)
public class SaTokenContextFilterForReactor implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return chain.filter(exchange)
.contextWrite(ctx -> SaReactorHolder.setContext(ctx, exchange, chain))
.doFinally(r -> {
// 在流式上下文中保存的数据会随着流式操作的结束而销毁,所以此处无需手动清除数据
});
}
}
/*
* 这种写法有问题:
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
try {
SaReactorSyncHolder.setContext(exchange);
return chain.filter(exchange);
} finally {
SaReactorSyncHolder.clearContext();
}
}
这种写法会先执行 finally然后进入具体的 Controller
*/

View File

@@ -1,196 +0,0 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.reactor.model;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.application.ApplicationInfo;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.reactor.context.SaReactorHolder;
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
import cn.dev33.satoken.util.SaFoxUtil;
import org.springframework.http.HttpCookie;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilterChain;
import java.util.Collection;
import java.util.Map;
/**
* 对 SaRequest 包装类的实现Reactor 响应式编程版)
*
* @author click33
* @since 1.19.0
*/
public class SaRequestForReactor implements SaRequest {
/**
* 底层Request对象
*/
protected ServerHttpRequest request;
/**
* 实例化
* @param request request对象
*/
public SaRequestForReactor(ServerHttpRequest request) {
this.request = request;
}
/**
* 获取底层源对象
*/
@Override
public Object getSource() {
return request;
}
/**
* 在 [请求体] 里获取一个值
*/
@Override
public String getParam(String name) {
return request.getQueryParams().getFirst(name);
}
/**
* 获取 [请求体] 里提交的所有参数名称
* @return 参数名称列表
*/
@Override
public Collection<String> getParamNames(){
return request.getQueryParams().keySet();
}
/**
* 获取 [请求体] 里提交的所有参数
* @return 参数列表
*/
@Override
public Map<String, String> getParamMap(){
return request.getQueryParams().toSingleValueMap();
}
/**
* 在 [请求头] 里获取一个值
*/
@Override
public String getHeader(String name) {
return request.getHeaders().getFirst(name);
}
/**
* 在 [Cookie作用域] 里获取一个值
*/
@Override
public String getCookieValue(String name) {
return getCookieLastValue(name);
}
/**
* 在 [ Cookie作用域 ] 里获取一个值 (第一个此名称的)
*/
@Override
public String getCookieFirstValue(String name){
HttpCookie cookie = request.getCookies().getFirst(name);
if(cookie == null) {
return null;
}
return cookie.getValue();
}
/**
* 在 [ Cookie作用域 ] 里获取一个值 (最后一个此名称的)
* @param name 键
* @return 值
*/
@Override
public String getCookieLastValue(String name){
String value = null;
String cookieStr = getHeader("Cookie");
if(SaFoxUtil.isNotEmpty(cookieStr)) {
String[] cookieItems = cookieStr.split(";");
for (String item : cookieItems) {
String[] kv = item.split("=");
if (kv.length == 2) {
if (kv[0].trim().equals(name)) {
value = kv[1].trim();
}
}
}
}
return value;
// 此种写法无法获取到最后一个 CookieWebFlux 底层代码应该是有bug前端提交多个同名Cookie时只能解析出第一个来
// List<HttpCookie> cookies = request.getCookies().get(name);
// if(cookies.isEmpty()) {
// return null;
// }
// return cookies.get(cookies.size() - 1).getValue();
}
/**
* 返回当前请求path (不包括上下文名称)
*/
@Override
public String getRequestPath() {
return ApplicationInfo.cutPathPrefix(request.getPath().toString());
}
/**
* 返回当前请求的urlhttp://xxx.com/test
* @return see note
*/
public String getUrl() {
String currDomain = SaManager.getConfig().getCurrDomain();
if( ! SaFoxUtil.isEmpty(currDomain)) {
return currDomain + this.getRequestPath();
}
return request.getURI().toString();
}
/**
* 返回当前请求的类型
*/
@Override
public String getMethod() {
return request.getMethod().name();
}
/**
* 查询请求 host
*/
@Override
public String getHost() {
return request.getURI().getHost();
}
/**
* 转发请求
*/
@Override
public Object forward(String path) {
ServerWebExchange exchange = SaReactorSyncHolder.getExchange();
WebFilterChain chain = exchange.getAttribute(SaReactorHolder.CHAIN_KEY);
ServerHttpRequest newRequest = request.mutate().path(path).build();
ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
return chain.filter(newExchange);
}
}

View File

@@ -1,93 +0,0 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.reactor.model;
import java.net.URI;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import cn.dev33.satoken.context.model.SaResponse;
/**
* 对 SaResponse 包装类的实现Reactor 响应式编程版)
*
* @author click33
* @since 1.19.0
*/
public class SaResponseForReactor implements SaResponse {
/**
* 底层Response对象
*/
protected ServerHttpResponse response;
/**
* 实例化
* @param response response对象
*/
public SaResponseForReactor(ServerHttpResponse response) {
this.response = response;
}
/**
* 获取底层源对象
*/
@Override
public Object getSource() {
return response;
}
/**
* 设置响应状态码
*/
@Override
public SaResponse setStatus(int sc) {
response.setStatusCode(HttpStatus.valueOf(sc));
return this;
}
/**
* 在响应头里写入一个值
*/
@Override
public SaResponse setHeader(String name, String value) {
response.getHeaders().set(name, value);
return this;
}
/**
* 在响应头里添加一个值
* @param name 名字
* @param value 值
* @return 对象自身
*/
public SaResponse addHeader(String name, String value) {
response.getHeaders().add(name, value);
return this;
}
/**
* 重定向
*/
@Override
public Object redirect(String url) {
response.setStatusCode(HttpStatus.FOUND);
response.getHeaders().setLocation(URI.create(url));
return null;
}
}

View File

@@ -1,77 +0,0 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.reactor.model;
import org.springframework.web.server.ServerWebExchange;
import cn.dev33.satoken.context.model.SaStorage;
/**
* 对 SaStorage 包装类的实现Reactor 响应式编程版)
*
* @author click33
* @since 1.19.0
*/
public class SaStorageForReactor implements SaStorage {
/**
* 底层 ServerWebExchange 对象
*/
protected ServerWebExchange exchange;
/**
* 实例化
* @param exchange exchange对象
*/
public SaStorageForReactor(ServerWebExchange exchange) {
this.exchange = exchange;
}
/**
* 获取底层源对象
*/
@Override
public Object getSource() {
return exchange;
}
/**
* 在 [Request作用域] 里写入一个值
*/
@Override
public SaStorageForReactor set(String key, Object value) {
exchange.getAttributes().put(key, value);
return this;
}
/**
* 在 [Request作用域] 里获取一个值
*/
@Override
public Object get(String key) {
return exchange.getAttributes().get(key);
}
/**
* 在 [Request作用域] 里删除一个值
*/
@Override
public SaStorageForReactor delete(String key) {
exchange.getAttributes().remove(key);
return this;
}
}

View File

@@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Sa-Token 集成 Reactor 响应式编程的各个组件
* Sa-Token 集成 Reactor 响应式编程 (SpringBoot 2.x)
*/
package cn.dev33.satoken.reactor;
package cn.dev33.satoken.reactor;

View File

@@ -5,7 +5,7 @@ import cn.dev33.satoken.util.SaFoxUtil;
import org.springframework.boot.SpringBootVersion;
/**
* SpringBoot 版本与 Sa-Token 版本兼容检查器,当开发者错误的在 SpringBoot3.x 项目中引入当前集成包时,将在控制台做出提醒并阻断项目启动
* SpringBoot 版本与 Sa-Token 版本兼容检查器,当开发者错误的在 SpringBoot3/4.x 项目中引入当前集成包时,将在控制台做出提醒并阻断项目启动
*
* @author Uncarbon
* @since 1.38.0
@@ -18,7 +18,7 @@ public class SpringBootVersionCompatibilityChecker {
return;
}
String str = "当前 SpringBoot 版本(" + version + ")与 Sa-Token 依赖不兼容," +
"请将依赖 sa-token-reactor-spring-boot-starter 修改为sa-token-reactor-spring-boot3-starter";
"请将依赖 sa-token-reactor-spring-boot-starter 修改为sa-token-reactor-spring-boot3/4-starter";
System.err.println(str);
throw new SaTokenException(str);
}

View File

@@ -16,83 +16,34 @@
<artifactId>sa-token-reactor-spring-boot3-starter</artifactId>
<description>springboot3 reactor integrate sa-token</description>
<properties>
<springboot3.version>3.0.1</springboot3.version>
</properties>
<dependencies>
<!-- spring-boot-starter (optional) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<optional>true</optional>
</dependency>
<!-- spring-web (optional) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<optional>true</optional>
</dependency>
<!-- reactor-core -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<!-- jackson-databind (optional) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<optional>true</optional>
</dependency>
<!-- config -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- sa-token-spring-boot-autoconfig -->
<!-- sa-token-spring-boot-reactor-v2v3v4-common -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-autoconfig</artifactId>
<artifactId>sa-token-spring-boot-reactor-v2v3v4-common</artifactId>
</dependency>
<!-- sa-token-jackson: JSON serialization -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jackson</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- spring-boot-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${springboot3.version}</version>
</dependency>
<!-- spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>6.2.5</version>
</dependency>
<!-- jackson-databind (optional) -->
<!--<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.1</version>
</dependency>-->
<!-- config (optional) -->
<!-- sa-token springboot3 相关依赖版本定义 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${springboot3.version}</version>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot3-dependencies</artifactId>
<version>${revision}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

View File

@@ -13,18 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.reactor.spring;
import cn.dev33.satoken.context.SaTokenContextForThreadLocal;
package cn.dev33.satoken.reactor;
/**
* <h2> 此为低版本(<1.42.0) 的上下文处理方案仅做留档如无必要请勿使用 </h2>
* 占位符
*
* Sa-Token 上下文处理器 [ Spring Reactor 版本实现 ] 基于 SaTokenContextForThreadLocal 定制
*
* @author click33
* @since 1.33.0
* @since 1.45.0
*/
public class SaTokenContextForSpringReactor extends SaTokenContextForThreadLocal {
public class Placeholder {
}

View File

@@ -1,74 +0,0 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.reactor.filter;
import cn.dev33.satoken.exception.BackResultException;
import cn.dev33.satoken.exception.FirewallCheckException;
import cn.dev33.satoken.exception.StopMatchException;
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
import cn.dev33.satoken.reactor.model.SaRequestForReactor;
import cn.dev33.satoken.reactor.model.SaResponseForReactor;
import cn.dev33.satoken.reactor.util.SaReactorOperateUtil;
import cn.dev33.satoken.strategy.SaFirewallStrategy;
import cn.dev33.satoken.util.SaTokenConsts;
import org.springframework.core.annotation.Order;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
/**
* 防火墙校验过滤器 (Reactor版)
*
* @author click33
* @since 1.37.0
*/
@Order(SaTokenConsts.FIREWALL_CHECK_FILTER_ORDER)
public class SaFirewallCheckFilterForReactor implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
SaRequestForReactor saRequest = new SaRequestForReactor(exchange.getRequest());
SaResponseForReactor saResponse = new SaResponseForReactor(exchange.getResponse());
try {
SaReactorSyncHolder.setContext(exchange);
SaFirewallStrategy.instance.check.execute(saRequest, saResponse, exchange);
}
catch (StopMatchException ignored) {}
catch (BackResultException e) {
return SaReactorOperateUtil.writeResult(exchange, e.getMessage());
}
// FirewallCheckException 异常则交由异常处理策略处理
catch (FirewallCheckException e) {
if(SaFirewallStrategy.instance.checkFailHandle == null) {
return SaReactorOperateUtil.writeResult(exchange, e.getMessage());
} else {
SaFirewallStrategy.instance.checkFailHandle.run(e, saRequest, saResponse, null);
return Mono.empty();
}
}
finally {
SaReactorSyncHolder.clearContext();
}
// 更多异常则不处理,交由 Web 框架处理
// 向下执行
return chain.filter(exchange);
}
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.reactor.spring;
import cn.dev33.satoken.reactor.filter.SaFirewallCheckFilterForReactor;
import cn.dev33.satoken.reactor.filter.SaTokenContextFilterForReactor;
import cn.dev33.satoken.reactor.filter.SaTokenCorsFilterForReactor;
import cn.dev33.satoken.spring.pathmatch.SaPathPatternParserUtil;
import cn.dev33.satoken.strategy.SaStrategy;
import org.springframework.context.annotation.Bean;
/**
* 注册 Sa-Token 所需要的 Bean
*
* @author click33
* @since 1.34.0
*/
public class SaTokenContextRegister {
public SaTokenContextRegister() {
// 重写路由匹配算法
SaStrategy.instance.routeMatcher = (pattern, path) -> {
return SaPathPatternParserUtil.match(pattern, path);
};
}
/**
* 上下文过滤器
*
* @return /
*/
@Bean
public SaTokenContextFilterForReactor saTokenContextFilterForServlet() {
return new SaTokenContextFilterForReactor();
}
/**
* CORS 跨域策略过滤器
*
* @return /
*/
@Bean
public SaTokenCorsFilterForReactor saTokenCorsFilterForReactor() {
return new SaTokenCorsFilterForReactor();
}
/**
* 防火墙过滤器
*
* @return /
*/
@Bean
public SaFirewallCheckFilterForReactor saFirewallCheckFilterForReactor() {
return new SaFirewallCheckFilterForReactor();
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.reactor.util;
import cn.dev33.satoken.util.SaTokenConsts;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* Reactor 操作工具类
*
* @author click33
* @since 1.42.0
*/
public class SaReactorOperateUtil {
/**
* 写入结果到输出流
* @param exchange /
* @param result /
* @return /
*/
public static Mono<Void> writeResult(ServerWebExchange exchange, String result) {
// 写入输出流
// 请注意此处默认 Content-Type 为 text/plain如果需要返回 JSON 信息,需要在 return 前自行设置 Content-Type 为 application/json
// 例如SaHolder.getResponse().setHeader("Content-Type", "application/json;charset=UTF-8");
if(exchange.getResponse().getHeaders().getFirst(SaTokenConsts.CONTENT_TYPE_KEY) == null) {
exchange.getResponse().getHeaders().set(SaTokenConsts.CONTENT_TYPE_KEY, SaTokenConsts.CONTENT_TYPE_TEXT_PLAIN);
}
return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(result.getBytes())));
}
}

View File

@@ -0,0 +1,50 @@
<?xml version='1.0' encoding='utf-8'?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-starter</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<packaging>jar</packaging>
<name>sa-token-reactor-spring-boot4-starter</name>
<artifactId>sa-token-reactor-spring-boot4-starter</artifactId>
<description>springboot4 reactor integrate sa-token</description>
<dependencies>
<!-- sa-token-spring-boot-reactor-v2v3v4-common -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-reactor-v2v3v4-common</artifactId>
</dependency>
<!-- sa-token-jackson3: JSON serialization for Spring Boot 4 (Jackson 3) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jackson3</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- sa-token springboot4 相关依赖版本定义 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot4-dependencies</artifactId>
<version>${revision}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.reactor;
/**
* 占位符
*
* @author click33
* @since 1.45.0
*/
public class Placeholder {
}

View File

@@ -0,0 +1,79 @@
<?xml version='1.0' encoding='utf-8'?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-starter</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<packaging>jar</packaging>
<name>sa-token-spring-boot-reactor-v2v3v4-common</name>
<artifactId>sa-token-spring-boot-reactor-v2v3v4-common</artifactId>
<description>sa-token springboot reactor v2/v3/v4 common</description>
<dependencies>
<!-- sa-token-core -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-core</artifactId>
</dependency>
<!-- spring-boot-starter (optional) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<optional>true</optional>
</dependency>
<!-- spring-web (optional) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<optional>true</optional>
</dependency>
<!-- reactor-core (optional) -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<optional>true</optional>
</dependency>
<!-- spring-boot-configuration-processor (optional) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- sa-token-spring-boot-webmvc-reactor-v2v3v4-common -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-webmvc-reactor-v2v3v4-common</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- 默认引入 sa-token springboot2 相关依赖版本定义上层可以继续引入其它版本定义来覆盖本层Maven 采用就近原则选择依赖版本 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot2-dependencies</artifactId>
<version>${revision}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@@ -16,4 +16,4 @@
/**
* Sa-Token 集成 Reactor 响应式编程的各个组件
*/
package cn.dev33.satoken.reactor;
package cn.dev33.satoken.reactor;

View File

@@ -36,14 +36,33 @@
<artifactId>sa-token-servlet</artifactId>
</dependency>
<!-- sa-token-spring-boot-autoconfig -->
<!-- sa-token-spring-boot-webmvc-reactor-v2v3v4-common -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-autoconfig</artifactId>
<artifactId>sa-token-spring-boot-webmvc-reactor-v2v3v4-common</artifactId>
</dependency>
<!-- sa-token-jackson: JSON serialization -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jackson</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- sa-token springboot2 相关依赖版本定义 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot2-dependencies</artifactId>
<version>${revision}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@@ -5,7 +5,7 @@ import cn.dev33.satoken.util.SaFoxUtil;
import org.springframework.boot.SpringBootVersion;
/**
* SpringBoot 版本与 Sa-Token 版本兼容检查器,当开发者错误的在 SpringBoot3.x 项目中引入当前集成包时,将在控制台做出提醒并阻断项目启动
* SpringBoot 版本与 Sa-Token 版本兼容检查器,当开发者错误的在 SpringBoot3/4.x 项目中引入当前集成包时,将在控制台做出提醒并阻断项目启动
*
* @author Uncarbon
* @since 1.38.0
@@ -18,7 +18,7 @@ public class SpringBootVersionCompatibilityChecker {
return;
}
String str = "当前 SpringBoot 版本(" + version + ")与 Sa-Token 依赖不兼容," +
"请将依赖 sa-token-spring-boot-starter 修改为sa-token-spring-boot3-starter";
"请将依赖 sa-token-spring-boot-starter 修改为sa-token-spring-boot3/4-starter";
System.err.println(str);
throw new SaTokenException(str);
}

View File

@@ -12,9 +12,9 @@
</parent>
<packaging>jar</packaging>
<name>sa-token-spring-boot-autoconfig</name>
<artifactId>sa-token-spring-boot-autoconfig</artifactId>
<description>sa-token-spring-boot-autoconfig</description>
<name>sa-token-spring-boot-webmvc-reactor-v2v3v4-common</name>
<artifactId>sa-token-spring-boot-webmvc-reactor-v2v3v4-common</artifactId>
<description>sa-token springboot webmvc/reactor v2/v3/v4 common</description>
<dependencies>
@@ -27,12 +27,6 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.7</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<optional>true</optional>
</dependency>
@@ -43,12 +37,6 @@
<optional>true</optional>
</dependency>
<!-- jackson 序列化、redis集成、SSO、OAuth2 等模块要用到,比较重要所以内置集成 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jackson</artifactId>
</dependency>
<!-- SSO (optional) -->
<dependency>
<groupId>cn.dev33</groupId>
@@ -80,5 +68,19 @@
</dependencies>
<dependencyManagement>
<dependencies>
<!-- 默认引入 sa-token springboot2 相关依赖版本定义上层可以继续引入其它版本定义来覆盖本层Maven 采用就近原则选择依赖版本 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot2-dependencies</artifactId>
<version>${revision}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

Some files were not shown because too many files have changed in this diff Show More