1
0
mirror of synced 2026-05-22 14:43:15 +00:00

refactor: 重构 springboot reactor 相关集成包,优化依赖关系

This commit is contained in:
click33
2026-02-27 06:18:06 +08:00
parent e8fe3a83c7
commit 6398ef4ca6
56 changed files with 210 additions and 2218 deletions
+5
View File
@@ -76,6 +76,11 @@
<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>
@@ -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() {
@@ -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
@@ -8,7 +8,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
* Sa-Token整合webflux 示例 (springboot4)
*
* @author click33
* @since 2023年1月3
* @since 2026年2月27
*
*/
@SpringBootApplication
@@ -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() {
@@ -20,27 +20,28 @@ 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
+1 -1
View File
@@ -53,7 +53,7 @@
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
@@ -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() {
@@ -19,6 +19,7 @@
<properties>
<springboot3.version>3.0.1</springboot3.version>
<spring-web.version>6.2.5</spring-web.version>
</properties>
<dependencyManagement>
@@ -30,12 +31,21 @@
<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>${spring-web.version}</version>
</dependency>
<!-- config (optional) -->
<dependency>
<groupId>org.springframework.boot</groupId>
+2 -1
View File
@@ -19,6 +19,7 @@
<properties>
<springboot4.version>4.0.3</springboot4.version>
<springboot4-spring.version>7.0.3</springboot4-spring.version>
</properties>
<dependencyManagement>
@@ -40,7 +41,7 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>7.0.3</version>
<version>${springboot4-spring.version}</version>
</dependency>
<!-- spring-boot-starter-webflux (for reactor) -->
<dependency>
+1
View File
@@ -21,6 +21,7 @@
<module>sa-token-servlet</module>
<module>sa-token-jakarta-servlet</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>
@@ -24,37 +24,10 @@
<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-webmvc-reactor-v2v3v4-common -->
<!-- sa-token-spring-boot-reactor-v2v3v4-common -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-webmvc-reactor-v2v3v4-common</artifactId>
<artifactId>sa-token-spring-boot-reactor-v2v3v4-common</artifactId>
</dependency>
<!-- sa-token-jackson: JSON serialization -->
@@ -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();
}
});
}
}
@@ -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();
}
}
}
@@ -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);
}
}
@@ -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
*/
@@ -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;
// 此种写法无法获取到最后一个 Cookie,WebFlux 底层代码应该是有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());
}
/**
* 返回当前请求的url,例:http://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);
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
@@ -1,30 +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.context.SaTokenContextForThreadLocal;
/**
* <h2> 此为低版本(<1.42.0) 的上下文处理方案,仅做留档,如无必要请勿使用 </h2>
*
* Sa-Token 上下文处理器 [ Spring Reactor 版本实现 ] ,基于 SaTokenContextForThreadLocal 定制
*
* @author click33
* @since 1.33.0
*/
public class SaTokenContextForSpringReactor extends SaTokenContextForThreadLocal {
}
@@ -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);
}
@@ -16,49 +16,12 @@
<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-webmvc-reactor-v2v3v4-common -->
<!-- sa-token-spring-boot-reactor-v2v3v4-common -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-webmvc-reactor-v2v3v4-common</artifactId>
<artifactId>sa-token-spring-boot-reactor-v2v3v4-common</artifactId>
</dependency>
<!-- sa-token-jackson: JSON serialization -->
@@ -71,27 +34,16 @@
<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>
<!-- 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>
@@ -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);
}
}
@@ -1,44 +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 -> {
// 在流式上下文中保存的数据会随着流式操作的结束而销毁,所以此处无需手动清除数据
});
}
}
@@ -1,60 +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.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 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 {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
try {
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());
}
finally {
SaReactorSyncHolder.clearContext();
}
return chain.filter(exchange);
}
}
@@ -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 3.x)
*/
package cn.dev33.satoken.reactor;
package cn.dev33.satoken.reactor;
@@ -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();
}
}
@@ -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())));
}
}
@@ -17,44 +17,11 @@
<description>springboot4 reactor integrate sa-token</description>
<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-webmvc-reactor-v2v3v4-common -->
<!-- sa-token-spring-boot-reactor-v2v3v4-common -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-webmvc-reactor-v2v3v4-common</artifactId>
<artifactId>sa-token-spring-boot-reactor-v2v3v4-common</artifactId>
</dependency>
<!-- sa-token-jackson3: JSON serialization for Spring Boot 4 (Jackson 3) -->
@@ -67,6 +34,7 @@
<dependencyManagement>
<dependencies>
<!-- sa-token springboot4 相关依赖版本定义 -->
<dependency>
<groupId>cn.dev33</groupId>
@@ -75,6 +43,7 @@
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
@@ -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();
}
});
}
}
@@ -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();
}
}
}
@@ -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);
}
}
@@ -1,149 +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.34.0
*/
@Order(SaTokenConsts.ASSEMBLY_ORDER)
public class SaReactorFilter implements SaFilter, WebFilter {
// ------------------------ 设置此过滤器 拦截 & 放行 的路由
/**
* 拦截路由
*/
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);
}
}
@@ -1,60 +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.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 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 {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
try {
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());
}
finally {
SaReactorSyncHolder.clearContext();
}
return chain.filter(exchange);
}
}
@@ -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.34.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;
// 此种写法无法获取到最后一个 Cookie,WebFlux 底层代码应该是有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());
}
/**
* 返回当前请求的url,例:http://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);
}
}
@@ -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.34.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;
}
}
@@ -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.34.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;
}
}
@@ -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 4.x)
*/
package cn.dev33.satoken.reactor;
@@ -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();
}
}
@@ -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())));
}
}
@@ -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>
@@ -13,18 +13,7 @@
* 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;
/**
* <h2> 此为低版本(<1.42.0) 的上下文处理方案仅做留档如无必要请勿使用 </h2>
*
* Sa-Token 上下文处理器 [ Spring Reactor 版本实现 ] 基于 SaTokenContextForThreadLocal 定制
*
* @author click33
* @since 1.33.0
* Sa-Token 集成 Reactor 响应式编程的各个组件
*/
public class SaTokenContextForSpringReactor extends SaTokenContextForThreadLocal {
}
package cn.dev33.satoken.reactor;
@@ -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);
}