From 55f0c94aece2db85d62924eb77c8e138a64afe72 Mon Sep 17 00:00:00 2001 From: click33 <2393584716@qq.com> Date: Sun, 6 Apr 2025 23:22:01 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E6=89=80?= =?UTF-8?q?=E6=9C=89=20starter=20=E7=BB=84=E4=BB=B6=E7=9A=84=20SaTokenCont?= =?UTF-8?q?ext=20=E4=B8=8A=E4=B8=8B=E6=96=87=E8=AF=BB=E5=86=99=E7=AD=96?= =?UTF-8?q?=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/cn/dev33/satoken/SaManager.java | 30 ++-- .../cn/dev33/satoken/context/SaHolder.java | 8 +- .../dev33/satoken/context/SaTokenContext.java | 16 +- .../context/SaTokenContextDefaultImpl.java | 5 - .../context/SaTokenContextForThreadLocal.java | 5 - .../cn/dev33/satoken/error/SaErrorCode.java | 5 + .../satoken/fun/SaRetGenericFunction.java | 26 ++- .../fun/strategy/SaRouteMatchFunction.java | 27 ++- .../cn/dev33/satoken/router/SaRouter.java | 8 +- .../cn/dev33/satoken/strategy/SaStrategy.java | 9 + .../cn/dev33/satoken/util/SaTokenConsts.java | 12 +- .../src/main/java/com/pj/SaTokenDemoApp.java | 1 + .../main/java/com/pj/test/TestController.java | 5 +- sa-token-demo/sa-token-demo-test/pom.xml | 6 +- .../java/com/pj/test/Test2Controller.java | 12 +- .../sa-token-demo-webflux-springboot3/pom.xml | 19 +-- .../java/com/pj/satoken/SaTokenConfigure.java | 8 +- .../main/java/com/pj/test/DefineRoutes.java | 18 +- .../java/com/pj/test/GlobalException.java | 45 +---- .../main/java/com/pj/test/TestController.java | 137 ++++++++++------ .../main/java/com/pj/test/UserService.java | 25 +++ .../src/main/java/com/pj/util/AjaxJson.java | 154 ------------------ sa-token-demo/sa-token-demo-webflux/pom.xml | 21 +-- .../com/pj/SaTokenWebfluxApplication.java | 3 +- .../java/com/pj/satoken/SaTokenConfigure.java | 4 +- .../main/java/com/pj/test/DefineRoutes.java | 18 +- .../java/com/pj/test/GlobalException.java | 45 +---- .../main/java/com/pj/test/TestController.java | 127 ++++++++++----- .../main/java/com/pj/test/UserService.java | 25 +++ .../src/main/java/com/pj/util/AjaxJson.java | 154 ------------------ sa-token-dependencies/pom.xml | 3 +- .../dubbo/SaTokenSecondContextForDubbo.java | 9 +- .../filter/SaTokenDubboConsumerFilter.java | 2 +- .../dubbo3/SaTokenSecondContextForDubbo3.java | 6 - .../filter/SaTokenDubbo3ConsumerFilter.java | 2 +- .../grpc/SaTokenSecondContextForGrpc.java | 6 - .../SaTokenGrpcClientInterceptor.java | 2 +- .../servlet/model/SaRequestForServlet.java | 2 +- .../util/SaJakartaServletOperateUtil.java | 48 ++++++ .../servlet/util/SaTokenContextUtil.java | 99 +++++++++++ .../satoken/jboot/SaTokenContextForJboot.java | 17 +- .../jfinal/SaTokenContextForJfinal.java | 17 +- .../satoken/jfinal/SaTokenPathFilter.java | 2 +- .../pom.xml | 26 +-- .../reactor/context/SaReactorHolder.java | 79 ++++++--- .../reactor/context/SaReactorSyncHolder.java | 43 +++-- .../SaFirewallCheckFilterForReactor.java | 17 +- .../reactor/filter/SaReactorFilter.java | 63 ++----- .../SaTokenContextFilterForReactor.java | 59 +++++++ .../reactor/model/SaRequestForReactor.java | 2 +- .../SaTokenContextForSpringReactor.java | 13 +- .../spring/SaTokenContextRegister.java | 30 ++-- ...SpringBootVersionCompatibilityChecker.java | 3 +- .../reactor/util/SaReactorOperateUtil.java | 46 ++++++ .../pom.xml | 35 +--- .../reactor/context/SaReactorHolder.java | 87 +++++++--- .../reactor/context/SaReactorSyncHolder.java | 49 +++--- .../SaFirewallCheckFilterForReactor.java | 19 ++- .../reactor/filter/SaReactorFilter.java | 67 ++------ .../SaTokenContextFilterForReactor.java | 44 +++++ .../reactor/model/SaRequestForReactor.java | 2 +- .../SaTokenContextForSpringReactor.java | 17 +- .../spring/SaTokenContextRegister.java | 19 ++- .../reactor/util/SaReactorOperateUtil.java | 46 ++++++ .../servlet/model/SaRequestForServlet.java | 2 +- .../servlet/util/SaServletOperateUtil.java | 48 ++++++ .../servlet/util/SaTokenContextUtil.java | 99 +++++++++++ .../dev33/satoken/solon/SaBeanRegister.java | 22 ++- .../cn/dev33/satoken/solon/SaSolonPlugin.java | 3 - .../SaFirewallCheckFilterForSolon.java | 12 +- .../SaTokenContextFilterForSolon.java | 41 +++++ .../solon/integration/SaTokenFilter.java | 27 ++- .../solon/integration/SaTokenInterceptor.java | 25 +-- .../solon/model/SaContextForSolon.java | 12 +- .../solon/model/SaRequestForSolon.java | 6 +- .../solon/model/SaResponseForSolon.java | 6 +- .../solon/model/SaStorageForSolon.java | 6 +- .../solon/util/SaSolonOperateUtil.java} | 36 ++-- .../solon/util/SaTokenContextUtil.java | 85 ++++++++++ .../SaFirewallCheckFilterForServlet.java | 23 +-- .../dev33/satoken/filter/SaServletFilter.java | 39 ++--- .../SaTokenContextFilterForServlet.java | 46 ++++++ .../spring/SaTokenContextForSpring.java | 75 --------- .../spring/SaTokenContextRegister.java | 29 ++-- ...SpringBootVersionCompatibilityChecker.java | 2 +- .../dev33/satoken/spring/SpringMVCUtil.java | 12 +- ...aFirewallCheckFilterForJakartaServlet.java | 22 +-- .../dev33/satoken/filter/SaServletFilter.java | 39 ++--- ...SaTokenContextFilterForJakartaServlet.java | 46 ++++++ ...TokenContextForSpringInJakartaServlet.java | 11 +- .../spring/SaTokenContextRegister.java | 29 ++-- .../dev33/satoken/spring/SpringMVCUtil.java | 10 +- 92 files changed, 1538 insertions(+), 1234 deletions(-) rename sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/error/SaReactorSpringBootErrorCode.java => sa-token-core/src/main/java/cn/dev33/satoken/fun/SaRetGenericFunction.java (62%) rename sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/error/SaReactorSpringBootErrorCode.java => sa-token-core/src/main/java/cn/dev33/satoken/fun/strategy/SaRouteMatchFunction.java (62%) create mode 100644 sa-token-demo/sa-token-demo-webflux-springboot3/src/main/java/com/pj/test/UserService.java delete mode 100644 sa-token-demo/sa-token-demo-webflux-springboot3/src/main/java/com/pj/util/AjaxJson.java create mode 100644 sa-token-demo/sa-token-demo-webflux/src/main/java/com/pj/test/UserService.java delete mode 100644 sa-token-demo/sa-token-demo-webflux/src/main/java/com/pj/util/AjaxJson.java create mode 100644 sa-token-starter/sa-token-jakarta-servlet/src/main/java/cn/dev33/satoken/servlet/util/SaJakartaServletOperateUtil.java create mode 100644 sa-token-starter/sa-token-jakarta-servlet/src/main/java/cn/dev33/satoken/servlet/util/SaTokenContextUtil.java create mode 100644 sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaTokenContextFilterForReactor.java create mode 100644 sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/util/SaReactorOperateUtil.java create mode 100644 sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaTokenContextFilterForReactor.java create mode 100644 sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/util/SaReactorOperateUtil.java create mode 100644 sa-token-starter/sa-token-servlet/src/main/java/cn/dev33/satoken/servlet/util/SaServletOperateUtil.java create mode 100644 sa-token-starter/sa-token-servlet/src/main/java/cn/dev33/satoken/servlet/util/SaTokenContextUtil.java create mode 100644 sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/integration/SaTokenContextFilterForSolon.java rename sa-token-starter/{sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/error/SaSpringBootErrorCode.java => sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/util/SaSolonOperateUtil.java} (54%) create mode 100644 sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/util/SaTokenContextUtil.java create mode 100644 sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/filter/SaTokenContextFilterForServlet.java delete mode 100644 sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextForSpring.java create mode 100644 sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/filter/SaTokenContextFilterForJakartaServlet.java diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/SaManager.java b/sa-token-core/src/main/java/cn/dev33/satoken/SaManager.java index b24795e0..085a078e 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/SaManager.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/SaManager.java @@ -22,6 +22,7 @@ import cn.dev33.satoken.config.SaTokenConfig; import cn.dev33.satoken.config.SaTokenConfigFactory; import cn.dev33.satoken.context.SaTokenContext; import cn.dev33.satoken.context.SaTokenContextDefaultImpl; +import cn.dev33.satoken.context.SaTokenContextForThreadLocal; import cn.dev33.satoken.context.second.SaTokenSecondContext; import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.dao.SaTokenDaoDefaultImpl; @@ -155,9 +156,16 @@ public class SaManager { SaTokenEventCenter.doRegisterComponent("SaTokenContext", saTokenContext); } public static SaTokenContext getSaTokenContext() { + if (saTokenContext == null) { + synchronized (SaManager.class) { + if (saTokenContext == null) { + SaManager.saTokenContext = new SaTokenContextForThreadLocal(); + } + } + } return saTokenContext; } - + /** * 二级上下文 SaTokenSecondContext */ @@ -169,29 +177,29 @@ public class SaManager { public static SaTokenSecondContext getSaTokenSecondContext() { return saTokenSecondContext; } - + /** * 获取一个可用的 SaTokenContext (按照一级上下文、二级上下文、默认上下文的顺序来判断) - * @return / + * @return / */ public static SaTokenContext getSaTokenContextOrSecond() { - + // s1. 一级Context可用时返回一级Context if(saTokenContext != null) { if(saTokenSecondContext == null || saTokenContext.isValid()) { - // 因为 isValid 是一个耗时操作,所以此处假定:二级Context为null的情况下无需验证一级Context有效性 - // 这样可以提升6倍左右的上下文获取速度 + // 因为 isValid 是一个耗时操作,所以此处假定:二级Context为null的情况下无需验证一级Context有效性 + // 这样可以提升6倍左右的上下文获取速度 return saTokenContext; } } - - // s2. 一级Context不可用时判断二级Context是否可用 + + // s2. 一级Context不可用时判断二级Context是否可用 if(saTokenSecondContext != null && saTokenSecondContext.isValid()) { return saTokenSecondContext; } - - // s3. 都不行,就返回默认的 Context - return SaTokenContextDefaultImpl.defaultContext; + + // s3. 都不行,就返回默认的 Context + return SaTokenContextDefaultImpl.defaultContext; } /** diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/context/SaHolder.java b/sa-token-core/src/main/java/cn/dev33/satoken/context/SaHolder.java index 3bec3cbc..62ceedc8 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/context/SaHolder.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/context/SaHolder.java @@ -36,7 +36,7 @@ public class SaHolder { * @return / */ public static SaTokenContext getContext() { - return SaManager.getSaTokenContextOrSecond(); + return SaManager.getSaTokenContext(); } /** @@ -46,7 +46,7 @@ public class SaHolder { * @return / */ public static SaRequest getRequest() { - return SaManager.getSaTokenContextOrSecond().getRequest(); + return SaManager.getSaTokenContext().getRequest(); } /** @@ -56,7 +56,7 @@ public class SaHolder { * @return / */ public static SaResponse getResponse() { - return SaManager.getSaTokenContextOrSecond().getResponse(); + return SaManager.getSaTokenContext().getResponse(); } /** @@ -66,7 +66,7 @@ public class SaHolder { * @return / */ public static SaStorage getStorage() { - return SaManager.getSaTokenContextOrSecond().getStorage(); + return SaManager.getSaTokenContext().getStorage(); } /** diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/context/SaTokenContext.java b/sa-token-core/src/main/java/cn/dev33/satoken/context/SaTokenContext.java index 3682a0bc..fd3f7e79 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/context/SaTokenContext.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/context/SaTokenContext.java @@ -16,8 +16,8 @@ package cn.dev33.satoken.context; import cn.dev33.satoken.context.model.SaRequest; -import cn.dev33.satoken.context.model.SaStorage; import cn.dev33.satoken.context.model.SaResponse; +import cn.dev33.satoken.context.model.SaStorage; /** * Sa-Token 上下文处理器 @@ -53,20 +53,6 @@ public interface SaTokenContext { */ SaStorage getStorage(); - /** - * 判断:指定路由匹配符是否可以匹配成功指定路径 - *
-	 *     判断规则由底层 web 框架决定,例如在 springboot 中:
-	 *     	- matchPath("/user/*", "/user/login")  返回: true
-	 *     	- matchPath("/user/*", "/article/edit")  返回: false
-	 * 
- * - * @param pattern 路由匹配符 - * @param path 需要匹配的路径 - * @return / - */ - boolean matchPath(String pattern, String path); - /** * 判断:在本次请求中,此上下文是否可用。 *

例如在部分 rpc 调用时, 一级上下文会返回 false,这时候框架就会选择使用二级上下文来处理请求

diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/context/SaTokenContextDefaultImpl.java b/sa-token-core/src/main/java/cn/dev33/satoken/context/SaTokenContextDefaultImpl.java index 80991a4d..70cc0976 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/context/SaTokenContextDefaultImpl.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/context/SaTokenContextDefaultImpl.java @@ -59,9 +59,4 @@ public class SaTokenContextDefaultImpl implements SaTokenContext { throw new SaTokenContextException(ERROR_MESSAGE).setCode(SaErrorCode.CODE_10001); } - @Override - public boolean matchPath(String pattern, String path) { - throw new SaTokenContextException(ERROR_MESSAGE).setCode(SaErrorCode.CODE_10001); - } - } diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/context/SaTokenContextForThreadLocal.java b/sa-token-core/src/main/java/cn/dev33/satoken/context/SaTokenContextForThreadLocal.java index 7a9c3bc6..fd5395c1 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/context/SaTokenContextForThreadLocal.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/context/SaTokenContextForThreadLocal.java @@ -49,11 +49,6 @@ public class SaTokenContextForThreadLocal implements SaTokenContext { return SaTokenContextForThreadLocalStorage.getStorage(); } - @Override - public boolean matchPath(String pattern, String path) { - return false; - } - @Override public boolean isValid() { return SaTokenContextForThreadLocalStorage.getBox() != null; diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/error/SaErrorCode.java b/sa-token-core/src/main/java/cn/dev33/satoken/error/SaErrorCode.java index a69af4b8..946fc88a 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/error/SaErrorCode.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/error/SaErrorCode.java @@ -227,4 +227,9 @@ public interface SaErrorCode { /** API Key 不属于指定用户 */ int CODE_12312 = 12312; + // ------------ + + /** 未实现具体的路由匹配策略 */ + int CODE_12401 = 12401; + } diff --git a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/error/SaReactorSpringBootErrorCode.java b/sa-token-core/src/main/java/cn/dev33/satoken/fun/SaRetGenericFunction.java similarity index 62% rename from sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/error/SaReactorSpringBootErrorCode.java rename to sa-token-core/src/main/java/cn/dev33/satoken/fun/SaRetGenericFunction.java index dccfffb6..327d0b48 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/error/SaReactorSpringBootErrorCode.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/fun/SaRetGenericFunction.java @@ -13,23 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package cn.dev33.satoken.reactor.error; +package cn.dev33.satoken.fun; /** - * 定义 sa-token-reactor-spring-boot-starter 所有异常细分状态码 - * + * 无形参、有返回值(泛型)的函数式接口,方便开发者进行 lambda 表达式风格调用 + * * @author click33 - * @since 1.33.0 + * @since 1.42.0 */ -public interface SaReactorSpringBootErrorCode { +@FunctionalInterface +public interface SaRetGenericFunction { + + /** + * 执行的方法 + * @return 返回值 + */ + T run(); - /** 对象转 JSON 字符串失败 */ - int CODE_20203 = 20203; - - /** JSON 字符串转 Map 失败 */ - int CODE_20204 = 20204; - - /** 默认的 Filter 异常处理函数 */ - int CODE_20205 = 20205; - } diff --git a/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/error/SaReactorSpringBootErrorCode.java b/sa-token-core/src/main/java/cn/dev33/satoken/fun/strategy/SaRouteMatchFunction.java similarity index 62% rename from sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/error/SaReactorSpringBootErrorCode.java rename to sa-token-core/src/main/java/cn/dev33/satoken/fun/strategy/SaRouteMatchFunction.java index c9e1378a..aa6aba28 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/error/SaReactorSpringBootErrorCode.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/fun/strategy/SaRouteMatchFunction.java @@ -13,23 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package cn.dev33.satoken.reactor.error; +package cn.dev33.satoken.fun.strategy; + +import java.util.function.BiFunction; /** - * 定义 sa-token-reactor3-spring-boot-starter 所有异常细分状态码 - * + * 函数式接口:路由匹配策略 + * + *

参数:pattern, path

+ *

返回:是否匹配

+ * * @author click33 - * @since 1.34.0 + * @since 1.42.0 */ -public interface SaReactorSpringBootErrorCode { - - /** 对象转 JSON 字符串失败 */ - int CODE_20203 = 20203; +@FunctionalInterface +public interface SaRouteMatchFunction extends BiFunction { - /** JSON 字符串转 Map 失败 */ - int CODE_20204 = 20204; - - /** 默认的 Filter 异常处理函数 */ - int CODE_20205 = 20205; - -} +} \ No newline at end of file diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/router/SaRouter.java b/sa-token-core/src/main/java/cn/dev33/satoken/router/SaRouter.java index 99002a3e..0d452831 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/router/SaRouter.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/router/SaRouter.java @@ -15,15 +15,15 @@ */ package cn.dev33.satoken.router; -import java.util.List; - -import cn.dev33.satoken.SaManager; import cn.dev33.satoken.context.SaHolder; import cn.dev33.satoken.exception.BackResultException; import cn.dev33.satoken.exception.StopMatchException; import cn.dev33.satoken.fun.SaFunction; import cn.dev33.satoken.fun.SaParamFunction; import cn.dev33.satoken.fun.SaParamRetFunction; +import cn.dev33.satoken.strategy.SaStrategy; + +import java.util.List; /** * 路由匹配操作工具类 @@ -55,7 +55,7 @@ public class SaRouter { * @return 是否匹配成功 */ public static boolean isMatch(String pattern, String path) { - return SaManager.getSaTokenContextOrSecond().matchPath(pattern, path); + return SaStrategy.instance.routeMatcher.apply(pattern, path); } /** diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/strategy/SaStrategy.java b/sa-token-core/src/main/java/cn/dev33/satoken/strategy/SaStrategy.java index 5e461a73..09726fbc 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/strategy/SaStrategy.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/strategy/SaStrategy.java @@ -16,6 +16,8 @@ package cn.dev33.satoken.strategy; import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.error.SaErrorCode; +import cn.dev33.satoken.exception.NotImplException; import cn.dev33.satoken.exception.SaTokenException; import cn.dev33.satoken.fun.strategy.*; import cn.dev33.satoken.session.SaSession; @@ -175,6 +177,13 @@ public final class SaStrategy { return new StpLogic(loginType); }; + /** + * 路由匹配策略 + */ + public SaRouteMatchFunction routeMatcher = (pattern, path) -> { + throw new NotImplException("未实现具体路由匹配策略").setCode(SaErrorCode.CODE_12401); + }; + // ----------------------- 重写策略 set连缀风格 diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/util/SaTokenConsts.java b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaTokenConsts.java index fcbb6307..bca48cd3 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/util/SaTokenConsts.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaTokenConsts.java @@ -194,7 +194,17 @@ public class SaTokenConsts { /** * 防火墙校验过滤器的注册顺序 */ - public static final int FIREWALL_CHECK_FILTER_ORDER = -1000; + public static final int FIREWALL_CHECK_FILTER_ORDER = -102; + + /** + * 跨域处理过滤器的注册顺序 + */ + public static final int CORS_FILTER_ORDER = -103; + + /** + * 上下文过滤器的注册顺序 + */ + public static final int SA_TOKEN_CONTEXT_FILTER_ORDER = -104; /** * Content-Type key diff --git a/sa-token-demo/sa-token-demo-solon/src/main/java/com/pj/SaTokenDemoApp.java b/sa-token-demo/sa-token-demo-solon/src/main/java/com/pj/SaTokenDemoApp.java index 8bc72dfa..43ec18b7 100644 --- a/sa-token-demo/sa-token-demo-solon/src/main/java/com/pj/SaTokenDemoApp.java +++ b/sa-token-demo/sa-token-demo-solon/src/main/java/com/pj/SaTokenDemoApp.java @@ -17,4 +17,5 @@ public class SaTokenDemoApp { Solon.start(SaTokenDemoApp.class, args); System.out.println("\n启动成功:Sa-Token配置如下:" + SaManager.getConfig()); } + } \ No newline at end of file diff --git a/sa-token-demo/sa-token-demo-springboot3-redis/src/main/java/com/pj/test/TestController.java b/sa-token-demo/sa-token-demo-springboot3-redis/src/main/java/com/pj/test/TestController.java index fc6d1844..b9f11ead 100644 --- a/sa-token-demo/sa-token-demo-springboot3-redis/src/main/java/com/pj/test/TestController.java +++ b/sa-token-demo/sa-token-demo-springboot3-redis/src/main/java/com/pj/test/TestController.java @@ -1,6 +1,7 @@ package com.pj.test; import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.servlet.util.SaTokenContextUtil; import cn.dev33.satoken.spring.SpringMVCUtil; import cn.dev33.satoken.util.SaResult; import org.springframework.web.bind.annotation.RequestMapping; @@ -18,7 +19,9 @@ public class TestController { // 测试 浏览器访问: http://localhost:8081/test/test @RequestMapping("test") public SaResult test() { - System.out.println("------------进来了"); + System.out.println("------------进来了"); + System.out.println(SpringMVCUtil.getRequest()); + System.out.println(SaTokenContextUtil.getRequest()); return SaResult.ok(); } diff --git a/sa-token-demo/sa-token-demo-test/pom.xml b/sa-token-demo/sa-token-demo-test/pom.xml index dac828ca..e128b06b 100644 --- a/sa-token-demo/sa-token-demo-test/pom.xml +++ b/sa-token-demo/sa-token-demo-test/pom.xml @@ -73,8 +73,12 @@ spring-boot-configuration-processor true + + org.springframework.boot + spring-boot-starter-actuator + - + diff --git a/sa-token-demo/sa-token-demo-test/src/main/java/com/pj/test/Test2Controller.java b/sa-token-demo/sa-token-demo-test/src/main/java/com/pj/test/Test2Controller.java index 24e0c033..faea0641 100644 --- a/sa-token-demo/sa-token-demo-test/src/main/java/com/pj/test/Test2Controller.java +++ b/sa-token-demo/sa-token-demo-test/src/main/java/com/pj/test/Test2Controller.java @@ -1,6 +1,7 @@ package com.pj.test; -import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.servlet.util.SaTokenContextUtil; +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; @@ -17,9 +18,12 @@ public class Test2Controller { @RequestMapping("/test") public SaResult test2() { - StpUtil.login(30003); - System.out.println(StpUtil.getSession().timeout()); - System.out.println(StpUtil.getStpLogic().getTokenSession(false)); + System.out.println(SpringMVCUtil.getRequest()); + System.out.println(SaTokenContextUtil.getRequest()); + +// StpUtil.login(30003); +// System.out.println(StpUtil.getSession().timeout()); +// System.out.println(StpUtil.getStpLogic().getTokenSession(false)); return SaResult.ok(); } diff --git a/sa-token-demo/sa-token-demo-webflux-springboot3/pom.xml b/sa-token-demo/sa-token-demo-webflux-springboot3/pom.xml index 262e149e..823ade1f 100644 --- a/sa-token-demo/sa-token-demo-webflux-springboot3/pom.xml +++ b/sa-token-demo/sa-token-demo-webflux-springboot3/pom.xml @@ -26,10 +26,6 @@ org.springframework.boot spring-boot-starter-webflux - - org.springframework.boot - spring-boot-starter-aop - @@ -37,21 +33,14 @@ sa-token-reactor-spring-boot3-starter ${sa-token.version} - - + + - - - - + cn.dev33 @@ -38,20 +34,13 @@ ${sa-token.version} - + - - - - + 2.7.18 3.4.3 - 3.1.4.RELEASE + 5.3.39 + 3.7.4 2.13.4.1 2.11.2 3.1.0 diff --git a/sa-token-plugin/sa-token-dubbo/src/main/java/cn/dev33/satoken/context/dubbo/SaTokenSecondContextForDubbo.java b/sa-token-plugin/sa-token-dubbo/src/main/java/cn/dev33/satoken/context/dubbo/SaTokenSecondContextForDubbo.java index c6dfa03f..90c6dee0 100644 --- a/sa-token-plugin/sa-token-dubbo/src/main/java/cn/dev33/satoken/context/dubbo/SaTokenSecondContextForDubbo.java +++ b/sa-token-plugin/sa-token-dubbo/src/main/java/cn/dev33/satoken/context/dubbo/SaTokenSecondContextForDubbo.java @@ -15,8 +15,6 @@ */ package cn.dev33.satoken.context.dubbo; -import org.apache.dubbo.rpc.RpcContext; - import cn.dev33.satoken.context.dubbo.model.SaRequestForDubbo; import cn.dev33.satoken.context.dubbo.model.SaResponseForDubbo; import cn.dev33.satoken.context.dubbo.model.SaStorageForDubbo; @@ -24,7 +22,7 @@ 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.second.SaTokenSecondContext; -import cn.dev33.satoken.exception.ApiDisabledException; +import org.apache.dubbo.rpc.RpcContext; /** * Sa-Token 二级上下文 [ Dubbo版本 ] @@ -49,11 +47,6 @@ public class SaTokenSecondContextForDubbo implements SaTokenSecondContext { return new SaStorageForDubbo(RpcContext.getContext()); } - @Override - public boolean matchPath(String pattern, String path) { - throw new ApiDisabledException(); - } - @Override public boolean isValid() { return RpcContext.getContext() != null; diff --git a/sa-token-plugin/sa-token-dubbo/src/main/java/cn/dev33/satoken/context/dubbo/filter/SaTokenDubboConsumerFilter.java b/sa-token-plugin/sa-token-dubbo/src/main/java/cn/dev33/satoken/context/dubbo/filter/SaTokenDubboConsumerFilter.java index 15aacb60..702372b7 100644 --- a/sa-token-plugin/sa-token-dubbo/src/main/java/cn/dev33/satoken/context/dubbo/filter/SaTokenDubboConsumerFilter.java +++ b/sa-token-plugin/sa-token-dubbo/src/main/java/cn/dev33/satoken/context/dubbo/filter/SaTokenDubboConsumerFilter.java @@ -48,7 +48,7 @@ public class SaTokenDubboConsumerFilter implements Filter { } // 2、调用前,向下传递会话Token - if(SaManager.getSaTokenContextOrSecond() != SaTokenContextDefaultImpl.defaultContext) { + if(SaManager.getSaTokenContext() != SaTokenContextDefaultImpl.defaultContext) { RpcContext.getContext().setAttachment(SaTokenConsts.JUST_CREATED, StpUtil.getTokenValueNotCut()); } diff --git a/sa-token-plugin/sa-token-dubbo3/src/main/java/cn/dev33/satoken/context/dubbo3/SaTokenSecondContextForDubbo3.java b/sa-token-plugin/sa-token-dubbo3/src/main/java/cn/dev33/satoken/context/dubbo3/SaTokenSecondContextForDubbo3.java index bd43ab98..d05ace84 100644 --- a/sa-token-plugin/sa-token-dubbo3/src/main/java/cn/dev33/satoken/context/dubbo3/SaTokenSecondContextForDubbo3.java +++ b/sa-token-plugin/sa-token-dubbo3/src/main/java/cn/dev33/satoken/context/dubbo3/SaTokenSecondContextForDubbo3.java @@ -22,7 +22,6 @@ 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.second.SaTokenSecondContext; -import cn.dev33.satoken.exception.ApiDisabledException; import org.apache.dubbo.rpc.RpcContext; /** @@ -48,11 +47,6 @@ public class SaTokenSecondContextForDubbo3 implements SaTokenSecondContext { return new SaStorageForDubbo3(RpcContext.getServiceContext()); } - @Override - public boolean matchPath(String pattern, String path) { - throw new ApiDisabledException(); - } - @Override public boolean isValid() { return RpcContext.getServiceContext() != null; diff --git a/sa-token-plugin/sa-token-dubbo3/src/main/java/cn/dev33/satoken/context/dubbo3/filter/SaTokenDubbo3ConsumerFilter.java b/sa-token-plugin/sa-token-dubbo3/src/main/java/cn/dev33/satoken/context/dubbo3/filter/SaTokenDubbo3ConsumerFilter.java index b54b48f7..83dce1ac 100644 --- a/sa-token-plugin/sa-token-dubbo3/src/main/java/cn/dev33/satoken/context/dubbo3/filter/SaTokenDubbo3ConsumerFilter.java +++ b/sa-token-plugin/sa-token-dubbo3/src/main/java/cn/dev33/satoken/context/dubbo3/filter/SaTokenDubbo3ConsumerFilter.java @@ -42,7 +42,7 @@ public class SaTokenDubbo3ConsumerFilter implements Filter { } // 1. 调用前,向下传递会话Token - if(SaManager.getSaTokenContextOrSecond() != SaTokenContextDefaultImpl.defaultContext) { + if(SaManager.getSaTokenContext() != SaTokenContextDefaultImpl.defaultContext) { RpcContext.getServiceContext().setAttachment(SaTokenConsts.JUST_CREATED, StpUtil.getTokenValueNotCut()); } diff --git a/sa-token-plugin/sa-token-grpc/src/main/java/cn/dev33/satoken/context/grpc/SaTokenSecondContextForGrpc.java b/sa-token-plugin/sa-token-grpc/src/main/java/cn/dev33/satoken/context/grpc/SaTokenSecondContextForGrpc.java index 729fd9d8..47f6d8c5 100644 --- a/sa-token-plugin/sa-token-grpc/src/main/java/cn/dev33/satoken/context/grpc/SaTokenSecondContextForGrpc.java +++ b/sa-token-plugin/sa-token-grpc/src/main/java/cn/dev33/satoken/context/grpc/SaTokenSecondContextForGrpc.java @@ -23,7 +23,6 @@ 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.second.SaTokenSecondContext; -import cn.dev33.satoken.exception.ApiDisabledException; /** * Sa-Token 上下文 [grpc版本] @@ -48,11 +47,6 @@ public class SaTokenSecondContextForGrpc implements SaTokenSecondContext { return new SaStorageForGrpc(); } - @Override - public boolean matchPath(String pattern, String path) { - throw new ApiDisabledException(); - } - @Override public boolean isValid() { return SaTokenGrpcContext.isNotNull(); diff --git a/sa-token-plugin/sa-token-grpc/src/main/java/cn/dev33/satoken/context/grpc/interceptor/SaTokenGrpcClientInterceptor.java b/sa-token-plugin/sa-token-grpc/src/main/java/cn/dev33/satoken/context/grpc/interceptor/SaTokenGrpcClientInterceptor.java index 5a2dee19..040e9f12 100644 --- a/sa-token-plugin/sa-token-grpc/src/main/java/cn/dev33/satoken/context/grpc/interceptor/SaTokenGrpcClientInterceptor.java +++ b/sa-token-plugin/sa-token-grpc/src/main/java/cn/dev33/satoken/context/grpc/interceptor/SaTokenGrpcClientInterceptor.java @@ -58,7 +58,7 @@ public class SaTokenGrpcClientInterceptor implements ClientInterceptor, Ordered // 调用前,传递会话Token String tokenValue = StpUtil.getTokenValue(); if (SaFoxUtil.isNotEmpty(tokenValue) - && SaManager.getSaTokenContextOrSecond() != SaTokenContextDefaultImpl.defaultContext) { + && SaManager.getSaTokenContext() != SaTokenContextDefaultImpl.defaultContext) { headers.put(GrpcContextConstants.SA_JUST_CREATED_NOT_PREFIX, tokenValue); } diff --git a/sa-token-starter/sa-token-jakarta-servlet/src/main/java/cn/dev33/satoken/servlet/model/SaRequestForServlet.java b/sa-token-starter/sa-token-jakarta-servlet/src/main/java/cn/dev33/satoken/servlet/model/SaRequestForServlet.java index ca6250a3..f65d569b 100644 --- a/sa-token-starter/sa-token-jakarta-servlet/src/main/java/cn/dev33/satoken/servlet/model/SaRequestForServlet.java +++ b/sa-token-starter/sa-token-jakarta-servlet/src/main/java/cn/dev33/satoken/servlet/model/SaRequestForServlet.java @@ -188,7 +188,7 @@ public class SaRequestForServlet implements SaRequest { @Override public Object forward(String path) { try { - HttpServletResponse response = (HttpServletResponse)SaManager.getSaTokenContextOrSecond().getResponse().getSource(); + HttpServletResponse response = (HttpServletResponse)SaManager.getSaTokenContext().getResponse().getSource(); request.getRequestDispatcher(path).forward(request, response); return null; } catch (ServletException | IOException e) { diff --git a/sa-token-starter/sa-token-jakarta-servlet/src/main/java/cn/dev33/satoken/servlet/util/SaJakartaServletOperateUtil.java b/sa-token-starter/sa-token-jakarta-servlet/src/main/java/cn/dev33/satoken/servlet/util/SaJakartaServletOperateUtil.java new file mode 100644 index 00000000..eb126491 --- /dev/null +++ b/sa-token-starter/sa-token-jakarta-servlet/src/main/java/cn/dev33/satoken/servlet/util/SaJakartaServletOperateUtil.java @@ -0,0 +1,48 @@ +/* + * 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.servlet.util; + +import cn.dev33.satoken.util.SaTokenConsts; +import jakarta.servlet.ServletResponse; + +import java.io.IOException; + +/** + * Jakarta Servlet 操作工具类 + * + * @author click33 + * @since 1.42.0 + */ +public class SaJakartaServletOperateUtil { + + /** + * 写入结果到输出流 + * @param response / + * @param result / + */ + public static void writeResult(ServletResponse response, String result) throws IOException { + // 写入输出流 + // 请注意此处默认 Content-Type 为 text/plain,如果需要返回 JSON 信息,需要在 return 前自行设置 Content-Type 为 application/json + // 例如:SaHolder.getResponse().setHeader("Content-Type", "application/json;charset=UTF-8"); + if(response.getContentType() == null) { + response.setContentType(SaTokenConsts.CONTENT_TYPE_TEXT_PLAIN); + } + response.getWriter().print(result); + response.getWriter().flush(); + } + + +} diff --git a/sa-token-starter/sa-token-jakarta-servlet/src/main/java/cn/dev33/satoken/servlet/util/SaTokenContextUtil.java b/sa-token-starter/sa-token-jakarta-servlet/src/main/java/cn/dev33/satoken/servlet/util/SaTokenContextUtil.java new file mode 100644 index 00000000..7908f8a8 --- /dev/null +++ b/sa-token-starter/sa-token-jakarta-servlet/src/main/java/cn/dev33/satoken/servlet/util/SaTokenContextUtil.java @@ -0,0 +1,99 @@ +/* + * 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.servlet.util; + +import cn.dev33.satoken.context.SaTokenContextForThreadLocalStorage; +import cn.dev33.satoken.context.SaTokenContextForThreadLocalStorage.Box; +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.fun.SaFunction; +import cn.dev33.satoken.servlet.model.SaRequestForServlet; +import cn.dev33.satoken.servlet.model.SaResponseForServlet; +import cn.dev33.satoken.servlet.model.SaStorageForServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + + +/** + * SaTokenContext 上下文读写工具类 + * + * @author click33 + * @since 1.42.0 + */ +public class SaTokenContextUtil { + + /** + * 写入当前上下文 + * @param request / + * @param response / + */ + public static void setContext(HttpServletRequest request, HttpServletResponse response) { + SaRequest req = new SaRequestForServlet(request); + SaResponse res = new SaResponseForServlet(response); + SaStorage stg = new SaStorageForServlet(request); + SaTokenContextForThreadLocalStorage.setBox(req, res, stg); + } + + /** + * 清除当前上下文 + */ + public static void clearContext() { + SaTokenContextForThreadLocalStorage.clearBox(); + } + + /** + * 写入上下文对象, 并在执行函数后将其清除 + * @param request / + * @param response / + * @param fun / + */ + public static void setContext(HttpServletRequest request, HttpServletResponse response, SaFunction fun) { + try { + setContext(request, response); + fun.run(); + } finally { + clearContext(); + } + } + + /** + * 获取当前 Box + * @return / + */ + public static Box getBox() { + return SaTokenContextForThreadLocalStorage.getBoxNotNull(); + } + + /** + * 获取当前 Request + * @return / + */ + public static HttpServletRequest getRequest() { + Box box = SaTokenContextForThreadLocalStorage.getBoxNotNull(); + return (HttpServletRequest) box.getRequest().getSource(); + } + + /** + * 获取当前 Response + * @return / + */ + public static HttpServletResponse getResponse() { + Box box = SaTokenContextForThreadLocalStorage.getBoxNotNull(); + return (HttpServletResponse) box.getResponse().getSource(); + } + +} diff --git a/sa-token-starter/sa-token-jboot-plugin/src/main/java/cn/dev33/satoken/jboot/SaTokenContextForJboot.java b/sa-token-starter/sa-token-jboot-plugin/src/main/java/cn/dev33/satoken/jboot/SaTokenContextForJboot.java index 253b8e88..974541f3 100644 --- a/sa-token-starter/sa-token-jboot-plugin/src/main/java/cn/dev33/satoken/jboot/SaTokenContextForJboot.java +++ b/sa-token-starter/sa-token-jboot-plugin/src/main/java/cn/dev33/satoken/jboot/SaTokenContextForJboot.java @@ -22,12 +22,21 @@ import cn.dev33.satoken.context.model.SaStorage; import cn.dev33.satoken.servlet.model.SaRequestForServlet; import cn.dev33.satoken.servlet.model.SaResponseForServlet; import cn.dev33.satoken.servlet.model.SaStorageForServlet; +import cn.dev33.satoken.strategy.SaStrategy; import io.jboot.web.controller.JbootControllerContext; /** * Sa-Token 上线文处理器 [Jboot 版本实现] */ public class SaTokenContextForJboot implements SaTokenContext { + + public SaTokenContextForJboot() { + // 重写路由匹配算法 + SaStrategy.instance.routeMatcher = (pattern, path) -> { + return PathAnalyzer.get(pattern).matches(path); + }; + } + /** * 获取当前请求的Request对象 */ @@ -52,14 +61,6 @@ public class SaTokenContextForJboot implements SaTokenContext { return new SaStorageForServlet(JbootControllerContext.get().getRequest()); } - /** - * 校验指定路由匹配符是否可以匹配成功指定路径 - */ - @Override - public boolean matchPath(String pattern, String path) { - return PathAnalyzer.get(pattern).matches(path); - } - @Override public boolean isValid() { return SaTokenContext.super.isValid(); diff --git a/sa-token-starter/sa-token-jfinal-plugin/src/main/java/cn/dev33/satoken/jfinal/SaTokenContextForJfinal.java b/sa-token-starter/sa-token-jfinal-plugin/src/main/java/cn/dev33/satoken/jfinal/SaTokenContextForJfinal.java index 6b5412cb..198137a4 100644 --- a/sa-token-starter/sa-token-jfinal-plugin/src/main/java/cn/dev33/satoken/jfinal/SaTokenContextForJfinal.java +++ b/sa-token-starter/sa-token-jfinal-plugin/src/main/java/cn/dev33/satoken/jfinal/SaTokenContextForJfinal.java @@ -22,11 +22,20 @@ import cn.dev33.satoken.context.model.SaStorage; import cn.dev33.satoken.servlet.model.SaRequestForServlet; import cn.dev33.satoken.servlet.model.SaResponseForServlet; import cn.dev33.satoken.servlet.model.SaStorageForServlet; +import cn.dev33.satoken.strategy.SaStrategy; /** * Sa-Token 上线文处理器 [Jfinal 版本实现] */ public class SaTokenContextForJfinal implements SaTokenContext { + + public SaTokenContextForJfinal() { + // 重写路由匹配算法 + SaStrategy.instance.routeMatcher = (pattern, path) -> { + return PathAnalyzer.get(pattern).matches(path); + }; + } + /** * 获取当前请求的Request对象 */ @@ -51,14 +60,6 @@ public class SaTokenContextForJfinal implements SaTokenContext { return new SaStorageForServlet(SaControllerContext.get().getRequest()); } - /** - * 校验指定路由匹配符是否可以匹配成功指定路径 - */ - @Override - public boolean matchPath(String pattern, String path) { - return PathAnalyzer.get(pattern).matches(path); - } - @Override public boolean isValid() { return SaTokenContext.super.isValid(); diff --git a/sa-token-starter/sa-token-jfinal-plugin/src/main/java/cn/dev33/satoken/jfinal/SaTokenPathFilter.java b/sa-token-starter/sa-token-jfinal-plugin/src/main/java/cn/dev33/satoken/jfinal/SaTokenPathFilter.java index df5d380d..47ce1087 100644 --- a/sa-token-starter/sa-token-jfinal-plugin/src/main/java/cn/dev33/satoken/jfinal/SaTokenPathFilter.java +++ b/sa-token-starter/sa-token-jfinal-plugin/src/main/java/cn/dev33/satoken/jfinal/SaTokenPathFilter.java @@ -16,9 +16,9 @@ package cn.dev33.satoken.jfinal; import cn.dev33.satoken.exception.SaTokenException; +import cn.dev33.satoken.filter.SaFilter; import cn.dev33.satoken.filter.SaFilterAuthStrategy; import cn.dev33.satoken.filter.SaFilterErrorStrategy; -import cn.dev33.satoken.filter.SaFilter; import java.util.ArrayList; import java.util.Arrays; diff --git a/sa-token-starter/sa-token-reactor-spring-boot-starter/pom.xml b/sa-token-starter/sa-token-reactor-spring-boot-starter/pom.xml index 72101e93..d61cd5c9 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot-starter/pom.xml +++ b/sa-token-starter/sa-token-reactor-spring-boot-starter/pom.xml @@ -23,7 +23,7 @@ spring-boot-starter true - + org.springframework @@ -31,11 +31,10 @@ true - + io.projectreactor reactor-core - true @@ -51,36 +50,25 @@ spring-boot-configuration-processor true - - - - cn.dev33 - sa-token-core - - + cn.dev33 sa-token-spring-boot-autoconfig - - + - + org.springframework spring-web - 5.3.7 + 5.3.39 - + diff --git a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/context/SaReactorHolder.java b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/context/SaReactorHolder.java index c03ea589..2c9db4cc 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/context/SaReactorHolder.java +++ b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/context/SaReactorHolder.java @@ -15,9 +15,12 @@ */ 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 全局引用 @@ -28,37 +31,67 @@ import reactor.core.publisher.Mono; public class SaReactorHolder { /** - * key + * ServerWebExchange key */ - public static final Class CONTEXT_KEY = ServerWebExchange.class; + public static final String EXCHANGE_KEY = "SA_REACTOR_EXCHANGE_KEY"; /** - * chain_key + * WebFilterChain key */ - public static final String CHAIN_KEY = "WEB_FILTER_CHAIN_KEY"; - + public static final String CHAIN_KEY = "SA_REACTOR__CHAIN_KEY"; + /** - * 获取上下文对象 - * @return see note + * 在流式上下文写入 ServerWebExchange + * @param ctx 必填 + * @param exchange 必填 + * @param chain 非必填 + * @return / */ - public static Mono getContext() { - // 从全局 Mono 获取 - return Mono.subscriberContext().map(ctx -> ctx.get(CONTEXT_KEY)); + public static Context setContext(Context ctx, ServerWebExchange exchange, WebFilterChain chain) { + return ctx + .put(EXCHANGE_KEY, exchange) + .put(CHAIN_KEY, chain); } - + /** - * 获取上下文对象, 并设置到同步上下文中 - * @return see note + * 在流式上下文获取 ServerWebExchange + * @param ctx / + * @return / */ - public static Mono getContextAndSetSync() { - // 从全局 Mono 获取 - return Mono.subscriberContext().map(ctx -> { - // 设置到sync中 - SaReactorSyncHolder.setContext(ctx.get(CONTEXT_KEY)); - return ctx.get(CONTEXT_KEY); - }).doFinally(r->{ - // 从sync中清除 - SaReactorSyncHolder.clearContext(); + 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 getMonoExchange() { + return Mono.deferContextual(ctx -> Mono.just(getExchange(ctx))); + } + + /** + * 将 exchange 写入到同步上下文中,并执行一段代码,执行完毕清除上下文 + * + * @return / + */ + public static Mono sync(SaRetGenericFunction fun) { + return Mono.deferContextual(ctx -> { + try { + SaReactorSyncHolder.setContext(ctx.get(EXCHANGE_KEY)); + return Mono.just(fun.run()); + } finally { + SaReactorSyncHolder.clearContext(); + } }); } diff --git a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/context/SaReactorSyncHolder.java b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/context/SaReactorSyncHolder.java index 4487ab16..62666f1b 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/context/SaReactorSyncHolder.java +++ b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/context/SaReactorSyncHolder.java @@ -15,17 +15,16 @@ */ package cn.dev33.satoken.reactor.context; -import org.springframework.web.server.ServerWebExchange; - import cn.dev33.satoken.context.SaTokenContextForThreadLocalStorage; import cn.dev33.satoken.context.SaTokenContextForThreadLocalStorage.Box; 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.fun.SaFunction; +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 全局引用 @@ -36,8 +35,8 @@ import cn.dev33.satoken.reactor.model.SaStorageForReactor; public class SaReactorSyncHolder { /** - * 写入上下文对象 - * @param exchange see note + * 在同步上下文写入 ServerWebExchange + * @param exchange / */ public static void setContext(ServerWebExchange exchange) { SaRequest request = new SaRequestForReactor(exchange.getRequest()); @@ -45,32 +44,32 @@ public class SaReactorSyncHolder { SaStorage storage = new SaStorageForReactor(exchange); SaTokenContextForThreadLocalStorage.setBox(request, response, storage); } - + /** - * 获取上下文对象 - * @return see note - */ - public static ServerWebExchange getContext() { - Box box = SaTokenContextForThreadLocalStorage.getBoxNotNull(); - return (ServerWebExchange)box.getStorage().getSource(); - } - - /** - * 清除上下文对象 + * 在同步上下文清除 ServerWebExchange */ public static void clearContext() { SaTokenContextForThreadLocalStorage.clearBox(); } - + /** - * 写入上下文对象, 并在执行函数后将其清除 - * @param exchange see note - * @param fun see note + * 在同步上下文获取 ServerWebExchange + * @return / */ - public static void setContext(ServerWebExchange exchange, SaFunction fun) { + public static ServerWebExchange getExchange() { + Box box = SaTokenContextForThreadLocalStorage.getBoxNotNull(); + return (ServerWebExchange)box.getStorage().getSource(); + } + + /** + * 将 exchange 写入到同步上下文中,并执行一段代码,执行完毕清除上下文 + * @param exchange / + * @param fun / + */ + public static R setContext(ServerWebExchange exchange, SaRetGenericFunction fun) { try { setContext(exchange); - fun.run(); + return fun.run(); } finally { clearContext(); } diff --git a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaFirewallCheckFilterForReactor.java b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaFirewallCheckFilterForReactor.java index cef0a73b..52a4b091 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaFirewallCheckFilterForReactor.java +++ b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaFirewallCheckFilterForReactor.java @@ -15,10 +15,13 @@ */ 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; @@ -43,21 +46,25 @@ public class SaFirewallCheckFilterForReactor implements WebFilter { SaResponseForReactor saResponse = new SaResponseForReactor(exchange.getResponse()); try { + SaReactorSyncHolder.setContext(exchange); SaFirewallStrategy.instance.check.execute(saRequest, saResponse, exchange); } - catch (StopMatchException e) { - // 如果是 StopMatchException 异常,代表通过了防火墙验证,进入 Controller + catch (StopMatchException ignored) {} + catch (BackResultException e) { + return SaReactorOperateUtil.writeResult(exchange, e.getMessage()); } + // FirewallCheckException 异常则交由异常处理策略处理 catch (FirewallCheckException e) { - // FirewallCheckException 异常则交由异常处理策略处理 if(SaFirewallStrategy.instance.checkFailHandle == null) { - exchange.getResponse().getHeaders().set(SaTokenConsts.CONTENT_TYPE_KEY, SaTokenConsts.CONTENT_TYPE_TEXT_PLAIN); - return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(e.getMessage().getBytes()))); + return SaReactorOperateUtil.writeResult(exchange, e.getMessage()); } else { SaFirewallStrategy.instance.checkFailHandle.run(e, saRequest, saResponse, null); return Mono.empty(); } } + finally { + SaReactorSyncHolder.clearContext(); + } // 更多异常则不处理,交由 Web 框架处理 // 向下执行 diff --git a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaReactorFilter.java b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaReactorFilter.java index 8d501e25..ac5e5e9e 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaReactorFilter.java +++ b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaReactorFilter.java @@ -21,9 +21,8 @@ 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.SaReactorHolder; import cn.dev33.satoken.reactor.context.SaReactorSyncHolder; -import cn.dev33.satoken.reactor.error.SaReactorSpringBootErrorCode; +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; @@ -37,7 +36,7 @@ import java.util.Arrays; import java.util.List; /** - * Reactor 全局鉴权过滤器 + * 全局鉴权过滤器 (基于 Reactor) *

* 默认优先级为 -100,尽量保证在其它过滤器之前执行 *

@@ -103,7 +102,7 @@ public class SaReactorFilter implements SaFilter, WebFilter { * 异常处理函数:每次[认证函数]发生异常时执行此函数 */ public SaFilterErrorStrategy error = e -> { - throw new SaTokenException(e).setCode(SaReactorSpringBootErrorCode.CODE_20205); + throw new SaTokenException(e); }; /** @@ -135,55 +134,25 @@ public class SaReactorFilter implements SaFilter, WebFilter { @Override public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { - - // 写入WebFilterChain对象 - exchange.getAttributes().put(SaReactorHolder.CHAIN_KEY, chain); - + // ---------- 全局认证处理 try { - // 写入全局上下文 (同步) SaReactorSyncHolder.setContext(exchange); - - // 执行全局过滤器 beforeAuth.run(null); - SaRouter.match(includeList).notMatch(excludeList).check(r -> { - auth.run(null); - }); - - } catch (StopMatchException e) { - // StopMatchException 异常代表:停止匹配,进入Controller - - } catch (Throwable e) { - // 1. 获取异常处理策略结果 - String result = (e instanceof BackResultException) ? e.getMessage() : String.valueOf(error.run(e)); - - // 2. 写入输出流 - // 请注意此处默认 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()))); - - } finally { - // 清除上下文 + 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(); } - // ---------- 执行 - - // 写入全局上下文 (同步) - SaReactorSyncHolder.setContext(exchange); - - // 执行 - return chain.filter(exchange).subscriberContext(ctx -> { - // 写入全局上下文 (异步) - ctx = ctx.put(SaReactorHolder.CONTEXT_KEY, exchange); - return ctx; - }).doFinally(r -> { - // 清除上下文 - SaReactorSyncHolder.clearContext(); - }); + return chain.filter(exchange); } - + } diff --git a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaTokenContextFilterForReactor.java b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaTokenContextFilterForReactor.java new file mode 100644 index 00000000..7fb415bb --- /dev/null +++ b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaTokenContextFilterForReactor.java @@ -0,0 +1,59 @@ +/* + * 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 filter(ServerWebExchange exchange, WebFilterChain chain) { + return chain.filter(exchange) + .contextWrite(ctx -> SaReactorHolder.setContext(ctx, exchange, chain)) + .doFinally(r -> { + // 在流式上下文中保存的数据会随着流式操作的结束而销毁,所以此处无需手动清除数据 + }); + } + +} + + + /* + * 这种写法有问题: + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + try { + SaReactorSyncHolder.setContext(exchange); + return chain.filter(exchange); + } finally { + SaReactorSyncHolder.clearContext(); + } + } + 这种写法会先执行 finally,然后进入具体的 Controller + */ diff --git a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/model/SaRequestForReactor.java b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/model/SaRequestForReactor.java index 98b4c6ea..bc1a3558 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/model/SaRequestForReactor.java +++ b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/model/SaRequestForReactor.java @@ -184,7 +184,7 @@ public class SaRequestForReactor implements SaRequest { */ @Override public Object forward(String path) { - ServerWebExchange exchange = SaReactorSyncHolder.getContext(); + ServerWebExchange exchange = SaReactorSyncHolder.getExchange(); WebFilterChain chain = exchange.getAttribute(SaReactorHolder.CHAIN_KEY); ServerHttpRequest newRequest = request.mutate().path(path).build(); diff --git a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextForSpringReactor.java b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextForSpringReactor.java index 82263785..46de9bcb 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextForSpringReactor.java +++ b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextForSpringReactor.java @@ -16,22 +16,15 @@ package cn.dev33.satoken.reactor.spring; import cn.dev33.satoken.context.SaTokenContextForThreadLocal; -import cn.dev33.satoken.spring.pathmatch.SaPathPatternParserUtil; /** + *

此为低版本(<1.42.0) 的上下文处理方案,仅做留档,如无必要请勿使用

+ * * Sa-Token 上下文处理器 [ Spring Reactor 版本实现 ] ,基于 SaTokenContextForThreadLocal 定制 * * @author click33 * @since 1.33.0 */ public class SaTokenContextForSpringReactor extends SaTokenContextForThreadLocal { - - /** - * 重写路由匹配方法 - */ - @Override - public boolean matchPath(String pattern, String path) { - return SaPathPatternParserUtil.match(pattern, path); - } - + } diff --git a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextRegister.java b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextRegister.java index 8d259b73..d5561274 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextRegister.java +++ b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextRegister.java @@ -16,10 +16,11 @@ package cn.dev33.satoken.reactor.spring; import cn.dev33.satoken.reactor.filter.SaFirewallCheckFilterForReactor; +import cn.dev33.satoken.reactor.filter.SaTokenContextFilterForReactor; +import cn.dev33.satoken.spring.pathmatch.SaPathPatternParserUtil; +import cn.dev33.satoken.strategy.SaStrategy; import org.springframework.context.annotation.Bean; -import cn.dev33.satoken.context.SaTokenContext; - /** * 注册 Sa-Token 所需要的 Bean * @@ -28,18 +29,25 @@ import cn.dev33.satoken.context.SaTokenContext; */ public class SaTokenContextRegister { - /** - * 获取上下文处理器组件 (Spring Reactor 版) - * - * @return / - */ - @Bean - public SaTokenContext getSaTokenContextForSpringReactor() { - return new SaTokenContextForSpringReactor(); + public SaTokenContextRegister() { + // 重写路由匹配算法 + SaStrategy.instance.routeMatcher = (pattern, path) -> { + return SaPathPatternParserUtil.match(pattern, path); + }; } /** - * 请求 path 校验过滤器 + * 上下文过滤器 + * + * @return / + */ + @Bean + public SaTokenContextFilterForReactor saTokenContextFilterForServlet() { + return new SaTokenContextFilterForReactor(); + } + + /** + * 防火墙过滤器 * * @return / */ diff --git a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SpringBootVersionCompatibilityChecker.java b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SpringBootVersionCompatibilityChecker.java index 266fa7e4..8afcf09a 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SpringBootVersionCompatibilityChecker.java +++ b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SpringBootVersionCompatibilityChecker.java @@ -5,7 +5,7 @@ import cn.dev33.satoken.util.SaFoxUtil; import org.springframework.boot.SpringBootVersion; /** - * SpringBoot 版本与 Sa-Token 版本兼容检查器 + * SpringBoot 版本与 Sa-Token 版本兼容检查器,当开发者错误的在 SpringBoot3.x 项目中引入当前集成包时,将在控制台做出提醒并阻断项目启动 * * @author Uncarbon * @since 1.38.0 @@ -22,4 +22,5 @@ public class SpringBootVersionCompatibilityChecker { System.err.println(str); throw new SaTokenException(str); } + } diff --git a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/util/SaReactorOperateUtil.java b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/util/SaReactorOperateUtil.java new file mode 100644 index 00000000..706501bf --- /dev/null +++ b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/util/SaReactorOperateUtil.java @@ -0,0 +1,46 @@ +/* + * 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 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()))); + } + +} diff --git a/sa-token-starter/sa-token-reactor-spring-boot3-starter/pom.xml b/sa-token-starter/sa-token-reactor-spring-boot3-starter/pom.xml index 40ed07c1..0c13412b 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot3-starter/pom.xml +++ b/sa-token-starter/sa-token-reactor-spring-boot3-starter/pom.xml @@ -32,15 +32,13 @@ org.springframework spring-web - true - + io.projectreactor reactor-core - true @@ -56,24 +54,13 @@ spring-boot-configuration-processor true - - - - cn.dev33 - sa-token-core - - + cn.dev33 sa-token-spring-boot-autoconfig - - + @@ -85,25 +72,19 @@ spring-boot-starter ${springboot3.version} - + org.springframework spring-web - 6.0.0 + 6.2.5 - - - io.projectreactor - reactor-core - 3.5.1 - - + - + diff --git a/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/context/SaReactorHolder.java b/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/context/SaReactorHolder.java index de66935e..6f81609c 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/context/SaReactorHolder.java +++ b/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/context/SaReactorHolder.java @@ -15,50 +15,83 @@ */ 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.34.0 + * @since 1.19.0 */ public class SaReactorHolder { - - /** - * key - */ - public static final Class CONTEXT_KEY = ServerWebExchange.class; /** - * chain_key + * ServerWebExchange key */ - public static final String CHAIN_KEY = "WEB_FILTER_CHAIN_KEY"; - + public static final String EXCHANGE_KEY = "SA_REACTOR_EXCHANGE_KEY"; + /** - * 获取上下文对象 - * @return see note + * WebFilterChain key */ - public static Mono getContext() { - // 从全局 Mono 获取 - return Mono.deferContextual(Mono::just).map(ctx -> ctx.get(CONTEXT_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); } - + /** - * 获取上下文对象, 并设置到同步上下文中 - * @return see note + * 在流式上下文获取 ServerWebExchange + * @param ctx / + * @return / */ - public static Mono getContextAndSetSync() { - // 从全局 Mono 获取 - return Mono.deferContextual(Mono::just).map(ctx -> { - // 设置到sync中 - SaReactorSyncHolder.setContext(ctx.get(CONTEXT_KEY)); - return ctx.get(CONTEXT_KEY); - }).doFinally(r->{ - // 从sync中清除 - SaReactorSyncHolder.clearContext(); + 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 getMonoExchange() { + return Mono.deferContextual(ctx -> Mono.just(getExchange(ctx))); + } + + /** + * 将 exchange 写入到同步上下文中,并执行一段代码,执行完毕清除上下文 + * + * @return / + */ + public static Mono sync(SaRetGenericFunction fun) { + return Mono.deferContextual(ctx -> { + try { + SaReactorSyncHolder.setContext(ctx.get(EXCHANGE_KEY)); + return Mono.just(fun.run()); + } finally { + SaReactorSyncHolder.clearContext(); + } }); } diff --git a/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/context/SaReactorSyncHolder.java b/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/context/SaReactorSyncHolder.java index 684a9253..c100ade8 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/context/SaReactorSyncHolder.java +++ b/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/context/SaReactorSyncHolder.java @@ -15,29 +15,28 @@ */ package cn.dev33.satoken.reactor.context; -import org.springframework.web.server.ServerWebExchange; - import cn.dev33.satoken.context.SaTokenContextForThreadLocalStorage; import cn.dev33.satoken.context.SaTokenContextForThreadLocalStorage.Box; 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.fun.SaFunction; +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.34.0 + * @since 1.19.0 */ public class SaReactorSyncHolder { - + /** - * 写入上下文对象 - * @param exchange see note + * 在同步上下文写入 ServerWebExchange + * @param exchange / */ public static void setContext(ServerWebExchange exchange) { SaRequest request = new SaRequestForReactor(exchange.getRequest()); @@ -45,35 +44,35 @@ public class SaReactorSyncHolder { SaStorage storage = new SaStorageForReactor(exchange); SaTokenContextForThreadLocalStorage.setBox(request, response, storage); } - + /** - * 获取上下文对象 - * @return see note - */ - public static ServerWebExchange getContext() { - Box box = SaTokenContextForThreadLocalStorage.getBoxNotNull(); - return (ServerWebExchange)box.getStorage().getSource(); - } - - /** - * 清除上下文对象 + * 在同步上下文清除 ServerWebExchange */ public static void clearContext() { SaTokenContextForThreadLocalStorage.clearBox(); } - + /** - * 写入上下文对象, 并在执行函数后将其清除 - * @param exchange see note - * @param fun see note + * 在同步上下文获取 ServerWebExchange + * @return / */ - public static void setContext(ServerWebExchange exchange, SaFunction fun) { + public static ServerWebExchange getExchange() { + Box box = SaTokenContextForThreadLocalStorage.getBoxNotNull(); + return (ServerWebExchange)box.getStorage().getSource(); + } + + /** + * 将 exchange 写入到同步上下文中,并执行一段代码,执行完毕清除上下文 + * @param exchange / + * @param fun / + */ + public static R setContext(ServerWebExchange exchange, SaRetGenericFunction fun) { try { setContext(exchange); - fun.run(); + return fun.run(); } finally { clearContext(); } } - + } diff --git a/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaFirewallCheckFilterForReactor.java b/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaFirewallCheckFilterForReactor.java index cef0a73b..da6d6859 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaFirewallCheckFilterForReactor.java +++ b/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaFirewallCheckFilterForReactor.java @@ -15,10 +15,13 @@ */ 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; @@ -43,25 +46,29 @@ public class SaFirewallCheckFilterForReactor implements WebFilter { SaResponseForReactor saResponse = new SaResponseForReactor(exchange.getResponse()); try { + SaReactorSyncHolder.setContext(exchange); SaFirewallStrategy.instance.check.execute(saRequest, saResponse, exchange); } - catch (StopMatchException e) { - // 如果是 StopMatchException 异常,代表通过了防火墙验证,进入 Controller + catch (StopMatchException ignored) {} + catch (BackResultException e) { + return SaReactorOperateUtil.writeResult(exchange, e.getMessage()); } + // FirewallCheckException 异常则交由异常处理策略处理 catch (FirewallCheckException e) { - // FirewallCheckException 异常则交由异常处理策略处理 if(SaFirewallStrategy.instance.checkFailHandle == null) { - exchange.getResponse().getHeaders().set(SaTokenConsts.CONTENT_TYPE_KEY, SaTokenConsts.CONTENT_TYPE_TEXT_PLAIN); - return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(e.getMessage().getBytes()))); + 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); } -} +} \ No newline at end of file diff --git a/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaReactorFilter.java b/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaReactorFilter.java index 276f8fa2..83c59151 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaReactorFilter.java +++ b/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaReactorFilter.java @@ -21,9 +21,8 @@ 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.SaReactorHolder; import cn.dev33.satoken.reactor.context.SaReactorSyncHolder; -import cn.dev33.satoken.reactor.error.SaReactorSpringBootErrorCode; +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; @@ -96,7 +95,7 @@ public class SaReactorFilter implements SaFilter, WebFilter { * 异常处理函数:每次[认证函数]发生异常时执行此函数 */ public SaFilterErrorStrategy error = e -> { - throw new SaTokenException(e).setCode(SaReactorSpringBootErrorCode.CODE_20205); + throw new SaTokenException(e); }; /** @@ -123,60 +122,28 @@ public class SaReactorFilter implements SaFilter, WebFilter { return this; } - // ------------------------ filter @Override public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { - - // 写入WebFilterChain对象 - exchange.getAttributes().put(SaReactorHolder.CHAIN_KEY, chain); - - // ---------- 全局认证处理 - try { - // 写入全局上下文 (同步) - SaReactorSyncHolder.setContext(exchange); - - // 执行全局过滤器 - beforeAuth.run(null); - SaRouter.match(includeList).notMatch(excludeList).check(r -> { - auth.run(null); - }); - - } catch (StopMatchException e) { - // StopMatchException 异常代表:停止匹配,进入Controller - } catch (Throwable e) { - // 1. 获取异常处理策略结果 - String result = (e instanceof BackResultException) ? e.getMessage() : String.valueOf(error.run(e)); - - // 2. 写入输出流 - // 请注意此处默认 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()))); - - } finally { - // 清除上下文 + // ---------- 全局认证处理 + 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(); } - // ---------- 执行 - - // 写入全局上下文 (同步) - SaReactorSyncHolder.setContext(exchange); - - // 执行 - return chain.filter(exchange).contextWrite(ctx -> { - // 写入全局上下文 (异步) - ctx = ctx.put(SaReactorHolder.CONTEXT_KEY, exchange); - return ctx; - }).doFinally(r -> { - // 清除上下文 - SaReactorSyncHolder.clearContext(); - }); + return chain.filter(exchange); } - } diff --git a/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaTokenContextFilterForReactor.java b/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaTokenContextFilterForReactor.java new file mode 100644 index 00000000..27936837 --- /dev/null +++ b/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaTokenContextFilterForReactor.java @@ -0,0 +1,44 @@ +/* + * 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 filter(ServerWebExchange exchange, WebFilterChain chain) { + return chain.filter(exchange) + .contextWrite(ctx -> SaReactorHolder.setContext(ctx, exchange, chain)) + .doFinally(r -> { + // 在流式上下文中保存的数据会随着流式操作的结束而销毁,所以此处无需手动清除数据 + }); + } + +} \ No newline at end of file diff --git a/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/model/SaRequestForReactor.java b/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/model/SaRequestForReactor.java index 41131b6f..ce2e4275 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/model/SaRequestForReactor.java +++ b/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/model/SaRequestForReactor.java @@ -184,7 +184,7 @@ public class SaRequestForReactor implements SaRequest { */ @Override public Object forward(String path) { - ServerWebExchange exchange = SaReactorSyncHolder.getContext(); + ServerWebExchange exchange = SaReactorSyncHolder.getExchange(); WebFilterChain chain = exchange.getAttribute(SaReactorHolder.CHAIN_KEY); ServerHttpRequest newRequest = request.mutate().path(path).build(); diff --git a/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextForSpringReactor.java b/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextForSpringReactor.java index c02dfbac..1b4e785a 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextForSpringReactor.java +++ b/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextForSpringReactor.java @@ -16,22 +16,15 @@ package cn.dev33.satoken.reactor.spring; import cn.dev33.satoken.context.SaTokenContextForThreadLocal; -import cn.dev33.satoken.spring.pathmatch.SaPathPatternParserUtil; /** + *

此为低版本(<1.42.0) 的上下文处理方案,仅做留档,如无必要请勿使用

+ * * Sa-Token 上下文处理器 [ Spring Reactor 版本实现 ] ,基于 SaTokenContextForThreadLocal 定制 - * + * * @author click33 - * @since 1.34.0 + * @since 1.33.0 */ public class SaTokenContextForSpringReactor extends SaTokenContextForThreadLocal { - - /** - * 重写路由匹配方法 - */ - @Override - public boolean matchPath(String pattern, String path) { - return SaPathPatternParserUtil.match(pattern, path); - } - + } diff --git a/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextRegister.java b/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextRegister.java index 04fe1d02..d5561274 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextRegister.java +++ b/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaTokenContextRegister.java @@ -15,8 +15,10 @@ */ package cn.dev33.satoken.reactor.spring; -import cn.dev33.satoken.context.SaTokenContext; import cn.dev33.satoken.reactor.filter.SaFirewallCheckFilterForReactor; +import cn.dev33.satoken.reactor.filter.SaTokenContextFilterForReactor; +import cn.dev33.satoken.spring.pathmatch.SaPathPatternParserUtil; +import cn.dev33.satoken.strategy.SaStrategy; import org.springframework.context.annotation.Bean; /** @@ -27,18 +29,25 @@ import org.springframework.context.annotation.Bean; */ public class SaTokenContextRegister { + public SaTokenContextRegister() { + // 重写路由匹配算法 + SaStrategy.instance.routeMatcher = (pattern, path) -> { + return SaPathPatternParserUtil.match(pattern, path); + }; + } + /** - * 获取上下文处理器组件 (Spring Reactor 版) + * 上下文过滤器 * * @return / */ @Bean - public SaTokenContext getSaTokenContextForSpringReactor() { - return new SaTokenContextForSpringReactor(); + public SaTokenContextFilterForReactor saTokenContextFilterForServlet() { + return new SaTokenContextFilterForReactor(); } /** - * 请求 path 校验过滤器 + * 防火墙过滤器 * * @return / */ diff --git a/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/util/SaReactorOperateUtil.java b/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/util/SaReactorOperateUtil.java new file mode 100644 index 00000000..706501bf --- /dev/null +++ b/sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/util/SaReactorOperateUtil.java @@ -0,0 +1,46 @@ +/* + * 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 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()))); + } + +} diff --git a/sa-token-starter/sa-token-servlet/src/main/java/cn/dev33/satoken/servlet/model/SaRequestForServlet.java b/sa-token-starter/sa-token-servlet/src/main/java/cn/dev33/satoken/servlet/model/SaRequestForServlet.java index 4b25c49b..131668ea 100644 --- a/sa-token-starter/sa-token-servlet/src/main/java/cn/dev33/satoken/servlet/model/SaRequestForServlet.java +++ b/sa-token-starter/sa-token-servlet/src/main/java/cn/dev33/satoken/servlet/model/SaRequestForServlet.java @@ -187,7 +187,7 @@ public class SaRequestForServlet implements SaRequest { @Override public Object forward(String path) { try { - HttpServletResponse response = (HttpServletResponse)SaManager.getSaTokenContextOrSecond().getResponse().getSource(); + HttpServletResponse response = (HttpServletResponse)SaManager.getSaTokenContext().getResponse().getSource(); request.getRequestDispatcher(path).forward(request, response); return null; } catch (ServletException | IOException e) { diff --git a/sa-token-starter/sa-token-servlet/src/main/java/cn/dev33/satoken/servlet/util/SaServletOperateUtil.java b/sa-token-starter/sa-token-servlet/src/main/java/cn/dev33/satoken/servlet/util/SaServletOperateUtil.java new file mode 100644 index 00000000..0390e357 --- /dev/null +++ b/sa-token-starter/sa-token-servlet/src/main/java/cn/dev33/satoken/servlet/util/SaServletOperateUtil.java @@ -0,0 +1,48 @@ +/* + * 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.servlet.util; + +import cn.dev33.satoken.util.SaTokenConsts; + +import javax.servlet.ServletResponse; +import java.io.IOException; + +/** + * Servlet 操作工具类 + * + * @author click33 + * @since 1.42.0 + */ +public class SaServletOperateUtil { + + /** + * 写入结果到输出流 + * @param response / + * @param result / + */ + public static void writeResult(ServletResponse response, String result) throws IOException { + // 写入输出流 + // 请注意此处默认 Content-Type 为 text/plain,如果需要返回 JSON 信息,需要在 return 前自行设置 Content-Type 为 application/json + // 例如:SaHolder.getResponse().setHeader("Content-Type", "application/json;charset=UTF-8"); + if(response.getContentType() == null) { + response.setContentType(SaTokenConsts.CONTENT_TYPE_TEXT_PLAIN); + } + response.getWriter().print(result); + response.getWriter().flush(); + } + + +} diff --git a/sa-token-starter/sa-token-servlet/src/main/java/cn/dev33/satoken/servlet/util/SaTokenContextUtil.java b/sa-token-starter/sa-token-servlet/src/main/java/cn/dev33/satoken/servlet/util/SaTokenContextUtil.java new file mode 100644 index 00000000..48bfcf59 --- /dev/null +++ b/sa-token-starter/sa-token-servlet/src/main/java/cn/dev33/satoken/servlet/util/SaTokenContextUtil.java @@ -0,0 +1,99 @@ +/* + * 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.servlet.util; + +import cn.dev33.satoken.context.SaTokenContextForThreadLocalStorage; +import cn.dev33.satoken.context.SaTokenContextForThreadLocalStorage.Box; +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.fun.SaFunction; +import cn.dev33.satoken.servlet.model.SaRequestForServlet; +import cn.dev33.satoken.servlet.model.SaResponseForServlet; +import cn.dev33.satoken.servlet.model.SaStorageForServlet; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * SaTokenContext 上下文读写工具类 + * + * @author click33 + * @since 1.42.0 + */ +public class SaTokenContextUtil { + + /** + * 写入当前上下文 + * @param request / + * @param response / + */ + public static void setContext(HttpServletRequest request, HttpServletResponse response) { + SaRequest req = new SaRequestForServlet(request); + SaResponse res = new SaResponseForServlet(response); + SaStorage stg = new SaStorageForServlet(request); + SaTokenContextForThreadLocalStorage.setBox(req, res, stg); + } + + /** + * 清除当前上下文 + */ + public static void clearContext() { + SaTokenContextForThreadLocalStorage.clearBox(); + } + + /** + * 写入上下文对象, 并在执行函数后将其清除 + * @param request / + * @param response / + * @param fun / + */ + public static void setContext(HttpServletRequest request, HttpServletResponse response, SaFunction fun) { + try { + setContext(request, response); + fun.run(); + } finally { + clearContext(); + } + } + + /** + * 获取当前 Box + * @return / + */ + public static Box getBox() { + return SaTokenContextForThreadLocalStorage.getBoxNotNull(); + } + + /** + * 获取当前 Request + * @return / + */ + public static HttpServletRequest getRequest() { + Box box = SaTokenContextForThreadLocalStorage.getBoxNotNull(); + return (HttpServletRequest) box.getRequest().getSource(); + } + + /** + * 获取当前 Response + * @return / + */ + public static HttpServletResponse getResponse() { + Box box = SaTokenContextForThreadLocalStorage.getBoxNotNull(); + return (HttpServletResponse) box.getResponse().getSource(); + } + +} diff --git a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/SaBeanRegister.java b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/SaBeanRegister.java index 206dcdcd..0738e9e7 100644 --- a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/SaBeanRegister.java +++ b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/SaBeanRegister.java @@ -17,11 +17,14 @@ package cn.dev33.satoken.solon; import cn.dev33.satoken.config.SaTokenConfig; import cn.dev33.satoken.solon.integration.SaFirewallCheckFilterForSolon; +import cn.dev33.satoken.solon.integration.SaTokenContextFilterForSolon; +import cn.dev33.satoken.strategy.SaStrategy; import cn.dev33.satoken.util.SaTokenConsts; import org.noear.solon.annotation.Bean; import org.noear.solon.annotation.Configuration; import org.noear.solon.annotation.Inject; import org.noear.solon.core.handle.Filter; +import org.noear.solon.core.util.PathAnalyzer; /** * 注册Sa-Token所需要的Bean @@ -32,6 +35,13 @@ import org.noear.solon.core.handle.Filter; @Configuration public class SaBeanRegister { + public SaBeanRegister() { + // 重写路由匹配算法 + SaStrategy.instance.routeMatcher = (pattern, path) -> { + return PathAnalyzer.get(pattern).matches(path); + }; + } + /** * 获取配置Bean * @@ -47,7 +57,17 @@ public class SaBeanRegister { } /** - * 防火墙校验过滤器 + * 上下文过滤器 + * + * @return / + */ + @Bean(index = SaTokenConsts.SA_TOKEN_CONTEXT_FILTER_ORDER) + public Filter saTokenContextFilterForSolon() { + return new SaTokenContextFilterForSolon(); + } + + /** + * 防火墙过滤器 * * @return / */ diff --git a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/SaSolonPlugin.java b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/SaSolonPlugin.java index ea8e9bbb..c0324868 100644 --- a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/SaSolonPlugin.java +++ b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/SaSolonPlugin.java @@ -17,7 +17,6 @@ package cn.dev33.satoken.solon; import cn.dev33.satoken.SaManager; import cn.dev33.satoken.solon.json.SaJsonTemplateForSnack3; -import cn.dev33.satoken.solon.model.SaContextForSolon; import cn.dev33.satoken.solon.oauth2.SaOAuth2BeanInject; import cn.dev33.satoken.solon.oauth2.SaOAuth2BeanRegister; import cn.dev33.satoken.solon.sso.SaSsoBeanInject; @@ -33,8 +32,6 @@ public class SaSolonPlugin implements Plugin { @Override public void start(AppContext context) { - // 注入上下文Bean - SaManager.setSaTokenContext(new SaContextForSolon()); // 注入JSON解析器Bean SaManager.setSaJsonTemplate(new SaJsonTemplateForSnack3()); diff --git a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/integration/SaFirewallCheckFilterForSolon.java b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/integration/SaFirewallCheckFilterForSolon.java index caf225d3..4bd14cc7 100644 --- a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/integration/SaFirewallCheckFilterForSolon.java +++ b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/integration/SaFirewallCheckFilterForSolon.java @@ -15,10 +15,12 @@ */ package cn.dev33.satoken.solon.integration; +import cn.dev33.satoken.exception.BackResultException; import cn.dev33.satoken.exception.FirewallCheckException; import cn.dev33.satoken.exception.StopMatchException; import cn.dev33.satoken.solon.model.SaRequestForSolon; import cn.dev33.satoken.solon.model.SaResponseForSolon; +import cn.dev33.satoken.solon.util.SaSolonOperateUtil; import cn.dev33.satoken.strategy.SaFirewallStrategy; import org.noear.solon.core.handle.Context; import org.noear.solon.core.handle.Filter; @@ -41,17 +43,17 @@ public class SaFirewallCheckFilterForSolon implements Filter { try { SaFirewallStrategy.instance.check.execute(saRequest, saResponse, null); } - catch (StopMatchException e) { - // 如果是 StopMatchException 异常,代表通过了防火墙验证,进入 Controller + catch (StopMatchException ignored) {} + catch (BackResultException e) { + SaSolonOperateUtil.writeResult(ctx, e.getMessage()); + return; } catch (FirewallCheckException e) { - // FirewallCheckException 异常则交由异常处理策略处理 if(SaFirewallStrategy.instance.checkFailHandle == null) { - ctx.render(e.getMessage()); + SaSolonOperateUtil.writeResult(ctx, e.getMessage()); } else { SaFirewallStrategy.instance.checkFailHandle.run(e, saRequest, saResponse, null); } - ctx.setHandled(true); return; } // 更多异常则不处理,交由 Web 框架处理 diff --git a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/integration/SaTokenContextFilterForSolon.java b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/integration/SaTokenContextFilterForSolon.java new file mode 100644 index 00000000..75e80b97 --- /dev/null +++ b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/integration/SaTokenContextFilterForSolon.java @@ -0,0 +1,41 @@ +/* + * 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.solon.integration; + +import cn.dev33.satoken.solon.util.SaTokenContextUtil; +import org.noear.solon.core.handle.Context; +import org.noear.solon.core.handle.Filter; +import org.noear.solon.core.handle.FilterChain; + +/** + * 上下文初始化过滤器 (基于 Solon) + * + * @author noear + * @since 1.42.0 + */ +public class SaTokenContextFilterForSolon implements Filter { + + @Override + public void doFilter(Context ctx, FilterChain chain) throws Throwable { + try { + SaTokenContextUtil.setContext(ctx); + chain.doFilter(ctx); + } finally { + SaTokenContextUtil.clearContext(); + } + } + +} diff --git a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/integration/SaTokenFilter.java b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/integration/SaTokenFilter.java index 0ebfecda..4d1324ee 100644 --- a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/integration/SaTokenFilter.java +++ b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/integration/SaTokenFilter.java @@ -18,10 +18,11 @@ package cn.dev33.satoken.solon.integration; 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.filter.SaFilter; import cn.dev33.satoken.router.SaRouter; +import cn.dev33.satoken.solon.util.SaSolonOperateUtil; import cn.dev33.satoken.strategy.SaAnnotationStrategy; import org.noear.solon.Solon; import org.noear.solon.core.handle.*; @@ -180,22 +181,14 @@ public class SaTokenFilter implements SaFilter, Filter { //之所以改名,为 auth.run(finalMainHandler); } }); - } catch (StopMatchException e) { - // StopMatchException 异常代表:停止匹配,进入Controller - } catch (SaTokenException e) { - // 1. 获取异常处理策略结果 - Object result; - if (e instanceof BackResultException) { - result = e.getMessage(); - } else { - result = error.run(e); - } - - // 2. 写入输出流 - if (result != null) { - ctx.render(result); - } - ctx.setHandled(true); + } + catch (StopMatchException ignored) {} + catch (BackResultException e) { + SaSolonOperateUtil.writeResult(ctx, e.getMessage()); + return; + } + catch (SaTokenException e) { + SaSolonOperateUtil.writeResult(ctx, error.run(e)); return; } diff --git a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/integration/SaTokenInterceptor.java b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/integration/SaTokenInterceptor.java index 62460cd7..78ba6569 100644 --- a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/integration/SaTokenInterceptor.java +++ b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/integration/SaTokenInterceptor.java @@ -22,6 +22,7 @@ import cn.dev33.satoken.filter.SaFilter; import cn.dev33.satoken.filter.SaFilterAuthStrategy; import cn.dev33.satoken.filter.SaFilterErrorStrategy; import cn.dev33.satoken.router.SaRouter; +import cn.dev33.satoken.solon.util.SaSolonOperateUtil; import cn.dev33.satoken.strategy.SaAnnotationStrategy; import org.noear.solon.core.handle.*; import org.noear.solon.core.route.RouterInterceptor; @@ -211,22 +212,14 @@ public class SaTokenInterceptor implements SaFilter, RouterInterceptor { } }); - } catch (StopMatchException e) { - // StopMatchException 异常代表:停止匹配,进入Controller - } catch (SaTokenException e) { - // 1. 获取异常处理策略结果 - Object result; - if (e instanceof BackResultException) { - result = e.getMessage(); - } else { - result = error.run(e); - } - - // 2. 写入输出流 - if (result != null) { - ctx.render(result); - } - ctx.setHandled(true); + } + catch (StopMatchException ignored) {} + catch (BackResultException e) { + SaSolonOperateUtil.writeResult(ctx, e.getMessage()); + return; + } + catch (SaTokenException e) { + SaSolonOperateUtil.writeResult(ctx, error.run(e)); return; } diff --git a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/model/SaContextForSolon.java b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/model/SaContextForSolon.java index 2194eea8..0c406035 100644 --- a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/model/SaContextForSolon.java +++ b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/model/SaContextForSolon.java @@ -20,13 +20,15 @@ import cn.dev33.satoken.context.model.SaRequest; import cn.dev33.satoken.context.model.SaResponse; import cn.dev33.satoken.context.model.SaStorage; import org.noear.solon.core.handle.Context; -import org.noear.solon.core.util.PathAnalyzer; /** + *

此为低版本(<1.42.0) 的上下文处理方案,基于 Solon 内部封装 Context.current() 读写上下文,仅做留档,如无必要请勿使用

+ * * @author noear * @since 1.4 */ public class SaContextForSolon implements SaTokenContext { + /** * 获取当前请求的Request对象 */ @@ -51,14 +53,6 @@ public class SaContextForSolon implements SaTokenContext { return new SaStorageForSolon(); } - /** - * 校验指定路由匹配符是否可以匹配成功指定路径 - */ - @Override - public boolean matchPath(String pattern, String path) { - return PathAnalyzer.get(pattern).matches(path); - } - /** * 此上下文是否有效 * @return / diff --git a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/model/SaRequestForSolon.java b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/model/SaRequestForSolon.java index cc6d2ef1..ae0af44b 100644 --- a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/model/SaRequestForSolon.java +++ b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/model/SaRequestForSolon.java @@ -32,7 +32,11 @@ public class SaRequestForSolon implements SaRequest { protected Context ctx; public SaRequestForSolon() { - ctx = Context.current(); + this(Context.current()); + } + + public SaRequestForSolon(Context ctx) { + this.ctx = ctx; } @Override diff --git a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/model/SaResponseForSolon.java b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/model/SaResponseForSolon.java index a591b1d3..dfb3748f 100644 --- a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/model/SaResponseForSolon.java +++ b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/model/SaResponseForSolon.java @@ -27,7 +27,11 @@ public class SaResponseForSolon implements SaResponse { protected Context ctx; public SaResponseForSolon() { - ctx = Context.current(); + this(Context.current()); + } + + public SaResponseForSolon(Context ctx) { + this.ctx = ctx; } @Override diff --git a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/model/SaStorageForSolon.java b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/model/SaStorageForSolon.java index 90fa90a2..08368723 100644 --- a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/model/SaStorageForSolon.java +++ b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/model/SaStorageForSolon.java @@ -27,7 +27,11 @@ public class SaStorageForSolon implements SaStorage { protected Context ctx; public SaStorageForSolon() { - ctx = Context.current(); + this(Context.current()); + } + + public SaStorageForSolon(Context ctx) { + this.ctx = ctx; } @Override diff --git a/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/error/SaSpringBootErrorCode.java b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/util/SaSolonOperateUtil.java similarity index 54% rename from sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/error/SaSpringBootErrorCode.java rename to sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/util/SaSolonOperateUtil.java index dee05007..05463a1c 100644 --- a/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/error/SaSpringBootErrorCode.java +++ b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/util/SaSolonOperateUtil.java @@ -13,29 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package cn.dev33.satoken.error; +package cn.dev33.satoken.solon.util; + +import org.noear.solon.core.handle.Context; /** - * 定义 sa-token-spring-boot-starter 所有异常细分状态码 - * + * Solon 操作工具类 + * * @author click33 - * @since 1.34.0 + * @since 1.42.0 */ -public interface SaSpringBootErrorCode { - - /** 企图在非 Web 上下文获取 Request、Response 等对象 */ - int CODE_20101 = 20101; +public class SaSolonOperateUtil { - /** 对象转 JSON 字符串失败 */ - int CODE_20103 = 20103; + /** + * 写入结果到输出流 + * @param ctx / + * @param result / + */ + public static void writeResult(Context ctx, Object result) throws Throwable { + if (result != null) { + ctx.render(result); + } + ctx.setHandled(true); + } - /** JSON 字符串转 Map 失败 */ - int CODE_20104 = 20104; - - /** JSON 字符串转 Object 失败 */ - int CODE_20106 = 20106; - - /** 默认的 Filter 异常处理函数 */ - int CODE_20105 = 20105; } diff --git a/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/util/SaTokenContextUtil.java b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/util/SaTokenContextUtil.java new file mode 100644 index 00000000..35f11de5 --- /dev/null +++ b/sa-token-starter/sa-token-solon-plugin/src/main/java/cn/dev33/satoken/solon/util/SaTokenContextUtil.java @@ -0,0 +1,85 @@ +/* + * 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.solon.util; + +import cn.dev33.satoken.context.SaTokenContextForThreadLocalStorage; +import cn.dev33.satoken.context.SaTokenContextForThreadLocalStorage.Box; +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.fun.SaFunction; +import cn.dev33.satoken.solon.model.SaRequestForSolon; +import cn.dev33.satoken.solon.model.SaResponseForSolon; +import cn.dev33.satoken.solon.model.SaStorageForSolon; +import org.noear.solon.core.handle.Context; + +/** + * SaTokenContext 上下文读写工具类 + * + * @author click33 + * @since 1.42.0 + */ +public class SaTokenContextUtil { + + /** + * 写入当前上下文 + */ + public static void setContext(Context ctx) { + SaRequest req = new SaRequestForSolon(ctx); + SaResponse res = new SaResponseForSolon(ctx); + SaStorage stg = new SaStorageForSolon(ctx); + SaTokenContextForThreadLocalStorage.setBox(req, res, stg); + } + + /** + * 清除当前上下文 + */ + public static void clearContext() { + SaTokenContextForThreadLocalStorage.clearBox(); + } + + /** + * 写入上下文对象, 并在执行函数后将其清除 + * @param ctx / + * @param fun / + */ + public static void setContext(Context ctx, SaFunction fun) { + try { + setContext(ctx); + fun.run(); + } finally { + clearContext(); + } + } + + /** + * 获取当前 Box + * @return / + */ + public static Box getBox() { + return SaTokenContextForThreadLocalStorage.getBoxNotNull(); + } + + /** + * 获取当前 Context + * @return / + */ + public static Context getContext() { + Box box = SaTokenContextForThreadLocalStorage.getBoxNotNull(); + return (Context) box.getStorage().getSource(); + } + +} diff --git a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/filter/SaFirewallCheckFilterForServlet.java b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/filter/SaFirewallCheckFilterForServlet.java index b3abf110..32f67259 100644 --- a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/filter/SaFirewallCheckFilterForServlet.java +++ b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/filter/SaFirewallCheckFilterForServlet.java @@ -15,10 +15,12 @@ */ package cn.dev33.satoken.filter; +import cn.dev33.satoken.exception.BackResultException; import cn.dev33.satoken.exception.FirewallCheckException; import cn.dev33.satoken.exception.StopMatchException; import cn.dev33.satoken.servlet.model.SaRequestForServlet; import cn.dev33.satoken.servlet.model.SaResponseForServlet; +import cn.dev33.satoken.servlet.util.SaServletOperateUtil; import cn.dev33.satoken.strategy.SaFirewallStrategy; import cn.dev33.satoken.util.SaTokenConsts; import org.springframework.core.annotation.Order; @@ -48,15 +50,14 @@ public class SaFirewallCheckFilterForServlet implements Filter { try { SaFirewallStrategy.instance.check.execute(saRequest, saResponse, null); } - catch (StopMatchException e) { - // 如果是 StopMatchException 异常,代表通过了防火墙验证,进入 Controller + catch (StopMatchException ignored) {} + catch (BackResultException e) { + SaServletOperateUtil.writeResult(response, e.getMessage()); + return; } catch (FirewallCheckException e) { - // FirewallCheckException 异常则交由异常处理策略处理 if(SaFirewallStrategy.instance.checkFailHandle == null) { - response.setContentType("text/plain; charset=utf-8"); - response.getWriter().print(e.getMessage()); - response.getWriter().flush(); + SaServletOperateUtil.writeResult(response, e.getMessage()); } else { SaFirewallStrategy.instance.checkFailHandle.run(e, saRequest, saResponse, null); } @@ -68,14 +69,4 @@ public class SaFirewallCheckFilterForServlet implements Filter { chain.doFilter(request, response); } - @Override - public void init(FilterConfig filterConfig) { - } - - @Override - public void destroy() { - } - - - } diff --git a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/filter/SaServletFilter.java b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/filter/SaServletFilter.java index 8fd3eb9f..bcb2e6e8 100644 --- a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/filter/SaServletFilter.java +++ b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/filter/SaServletFilter.java @@ -15,11 +15,11 @@ */ package cn.dev33.satoken.filter; -import cn.dev33.satoken.error.SaSpringBootErrorCode; import cn.dev33.satoken.exception.BackResultException; import cn.dev33.satoken.exception.SaTokenException; import cn.dev33.satoken.exception.StopMatchException; import cn.dev33.satoken.router.SaRouter; +import cn.dev33.satoken.servlet.util.SaServletOperateUtil; import cn.dev33.satoken.util.SaTokenConsts; import org.springframework.core.annotation.Order; @@ -30,7 +30,7 @@ import java.util.Arrays; import java.util.List; /** - * Servlet 全局鉴权过滤器 + * 全局鉴权过滤器 (基于 Servlet) *

* 默认优先级为 -100,尽量保证在其它过滤器之前执行 *

@@ -89,7 +89,7 @@ public class SaServletFilter implements SaFilter, Filter { * 异常处理函数:每次[认证函数]发生异常时执行此函数 */ public SaFilterErrorStrategy error = e -> { - throw new SaTokenException(e).setCode(SaSpringBootErrorCode.CODE_20105); + throw new SaTokenException(e); }; /** @@ -128,21 +128,14 @@ public class SaServletFilter implements SaFilter, Filter { SaRouter.match(includeList).notMatch(excludeList).check(r -> { auth.run(null); }); - - } catch (StopMatchException e) { - // StopMatchException 异常代表:停止匹配,进入Controller - - } catch (Throwable e) { - // 1. 获取异常处理策略结果 - String result = (e instanceof BackResultException) ? e.getMessage() : String.valueOf(error.run(e)); - - // 2. 写入输出流 - // 请注意此处默认 Content-Type 为 text/plain,如果需要返回 JSON 信息,需要在 return 前自行设置 Content-Type 为 application/json - // 例如:SaHolder.getResponse().setHeader("Content-Type", "application/json;charset=UTF-8"); - if(response.getContentType() == null) { - response.setContentType(SaTokenConsts.CONTENT_TYPE_TEXT_PLAIN); - } - response.getWriter().print(result); + } + catch (StopMatchException ignored) {} + catch (BackResultException e) { + SaServletOperateUtil.writeResult(response, e.getMessage()); + return; + } + catch (Throwable e) { + SaServletOperateUtil.writeResult(response, String.valueOf(error.run(e))); return; } @@ -150,14 +143,4 @@ public class SaServletFilter implements SaFilter, Filter { chain.doFilter(request, response); } - @Override - public void init(FilterConfig filterConfig) { - } - - @Override - public void destroy() { - } - - - } diff --git a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/filter/SaTokenContextFilterForServlet.java b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/filter/SaTokenContextFilterForServlet.java new file mode 100644 index 00000000..73bee39f --- /dev/null +++ b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/filter/SaTokenContextFilterForServlet.java @@ -0,0 +1,46 @@ +/* + * 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.filter; + +import cn.dev33.satoken.servlet.util.SaTokenContextUtil; +import cn.dev33.satoken.util.SaTokenConsts; +import org.springframework.core.annotation.Order; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * SaTokenContext 上下文初始化过滤器 (基于 Servlet) + * + * @author click33 + * @since 1.42.0 + */ +@Order(SaTokenConsts.SA_TOKEN_CONTEXT_FILTER_ORDER) +public class SaTokenContextFilterForServlet implements Filter { + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + try { + SaTokenContextUtil.setContext((HttpServletRequest) request, (HttpServletResponse) response); + chain.doFilter(request, response); + } finally { + SaTokenContextUtil.clearContext(); + } + } + +} diff --git a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextForSpring.java b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextForSpring.java deleted file mode 100644 index dd826350..00000000 --- a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextForSpring.java +++ /dev/null @@ -1,75 +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.spring; - -import cn.dev33.satoken.context.SaTokenContext; -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.servlet.model.SaRequestForServlet; -import cn.dev33.satoken.servlet.model.SaResponseForServlet; -import cn.dev33.satoken.servlet.model.SaStorageForServlet; -import cn.dev33.satoken.spring.pathmatch.SaPatternsRequestConditionHolder; - -/** - * Sa-Token 上下文处理器 [ SpringMVC版本实现 ]。在 SpringMVC、SpringBoot 中使用 Sa-Token 时,必须注入此实现类,否则会出现上下文无效异常 - * - * @author click33 - * @since 1.19.0 - */ -public class SaTokenContextForSpring implements SaTokenContext { - - /** - * 获取当前请求的 Request 包装对象 - */ - @Override - public SaRequest getRequest() { - return new SaRequestForServlet(SpringMVCUtil.getRequest()); - } - - /** - * 获取当前请求的 Response 包装对象 - */ - @Override - public SaResponse getResponse() { - return new SaResponseForServlet(SpringMVCUtil.getResponse()); - } - - /** - * 获取当前请求的 Storage 包装对象 - */ - @Override - public SaStorage getStorage() { - return new SaStorageForServlet(SpringMVCUtil.getRequest()); - } - - /** - * 判断:指定路由匹配符是否可以匹配成功指定路径 - */ - @Override - public boolean matchPath(String pattern, String path) { - return SaPatternsRequestConditionHolder.match(pattern, path); - } - - /** - * 判断:在本次请求中,此上下文是否可用。 - */ - @Override - public boolean isValid() { - return SpringMVCUtil.isWeb(); - } - -} diff --git a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextRegister.java b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextRegister.java index e8851475..4f53b570 100644 --- a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextRegister.java +++ b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextRegister.java @@ -15,8 +15,10 @@ */ package cn.dev33.satoken.spring; -import cn.dev33.satoken.context.SaTokenContext; import cn.dev33.satoken.filter.SaFirewallCheckFilterForServlet; +import cn.dev33.satoken.filter.SaTokenContextFilterForServlet; +import cn.dev33.satoken.spring.pathmatch.SaPatternsRequestConditionHolder; +import cn.dev33.satoken.strategy.SaStrategy; import org.springframework.context.annotation.Bean; /** @@ -27,18 +29,25 @@ import org.springframework.context.annotation.Bean; */ public class SaTokenContextRegister { - /** - * 获取上下文处理器组件 (Spring版) - * - * @return / - */ - @Bean - public SaTokenContext getSaTokenContextForSpring() { - return new SaTokenContextForSpring(); + public SaTokenContextRegister() { + // 重写路由匹配算法 + SaStrategy.instance.routeMatcher = (pattern, path) -> { + return SaPatternsRequestConditionHolder.match(pattern, path); + }; } /** - * 请求 path 校验过滤器 + * 上下文过滤器 + * + * @return / + */ + @Bean + public SaTokenContextFilterForServlet saTokenContextFilterForServlet() { + return new SaTokenContextFilterForServlet(); + } + + /** + * 防火墙过滤器 * * @return / */ diff --git a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SpringBootVersionCompatibilityChecker.java b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SpringBootVersionCompatibilityChecker.java index 755c274c..721fddfe 100644 --- a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SpringBootVersionCompatibilityChecker.java +++ b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SpringBootVersionCompatibilityChecker.java @@ -5,7 +5,7 @@ import cn.dev33.satoken.util.SaFoxUtil; import org.springframework.boot.SpringBootVersion; /** - * SpringBoot 版本与 Sa-Token 版本兼容检查器 + * SpringBoot 版本与 Sa-Token 版本兼容检查器,当开发者错误的在 SpringBoot3.x 项目中引入当前集成包时,将在控制台做出提醒并阻断项目启动 * * @author Uncarbon * @since 1.38.0 diff --git a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SpringMVCUtil.java b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SpringMVCUtil.java index 16514c5d..75cad04c 100644 --- a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SpringMVCUtil.java +++ b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SpringMVCUtil.java @@ -15,14 +15,12 @@ */ package cn.dev33.satoken.spring; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - +import cn.dev33.satoken.exception.NotWebContextException; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; -import cn.dev33.satoken.error.SaSpringBootErrorCode; -import cn.dev33.satoken.exception.NotWebContextException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; /** * SpringMVC 相关操作工具类,快速获取当前会话的 HttpServletRequest、HttpServletResponse 对象 @@ -42,7 +40,7 @@ public class SpringMVCUtil { public static HttpServletRequest getRequest() { ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if(servletRequestAttributes == null) { - throw new NotWebContextException("非 web 上下文无法获取 HttpServletRequest").setCode(SaSpringBootErrorCode.CODE_20101); + throw new NotWebContextException("非 web 上下文无法获取 HttpServletRequest"); } return servletRequestAttributes.getRequest(); } @@ -54,7 +52,7 @@ public class SpringMVCUtil { public static HttpServletResponse getResponse() { ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if(servletRequestAttributes == null) { - throw new NotWebContextException("非 web 上下文无法获取 HttpServletResponse").setCode(SaSpringBootErrorCode.CODE_20101); + throw new NotWebContextException("非 web 上下文无法获取 HttpServletResponse"); } return servletRequestAttributes.getResponse(); } diff --git a/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/filter/SaFirewallCheckFilterForJakartaServlet.java b/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/filter/SaFirewallCheckFilterForJakartaServlet.java index cb162ba0..b279eef1 100644 --- a/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/filter/SaFirewallCheckFilterForJakartaServlet.java +++ b/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/filter/SaFirewallCheckFilterForJakartaServlet.java @@ -15,10 +15,12 @@ */ package cn.dev33.satoken.filter; +import cn.dev33.satoken.exception.BackResultException; import cn.dev33.satoken.exception.FirewallCheckException; import cn.dev33.satoken.exception.StopMatchException; import cn.dev33.satoken.servlet.model.SaRequestForServlet; import cn.dev33.satoken.servlet.model.SaResponseForServlet; +import cn.dev33.satoken.servlet.util.SaJakartaServletOperateUtil; import cn.dev33.satoken.strategy.SaFirewallStrategy; import cn.dev33.satoken.util.SaTokenConsts; import jakarta.servlet.*; @@ -48,15 +50,14 @@ public class SaFirewallCheckFilterForJakartaServlet implements Filter { try { SaFirewallStrategy.instance.check.execute(saRequest, saResponse, null); } - catch (StopMatchException e) { - // 如果是 StopMatchException 异常,代表通过了防火墙验证,进入 Controller + catch (StopMatchException ignored) {} + catch (BackResultException e) { + SaJakartaServletOperateUtil.writeResult(response, e.getMessage()); + return; } catch (FirewallCheckException e) { - // FirewallCheckException 异常则交由异常处理策略处理 if(SaFirewallStrategy.instance.checkFailHandle == null) { - response.setContentType("text/plain; charset=utf-8"); - response.getWriter().print(e.getMessage()); - response.getWriter().flush(); + SaJakartaServletOperateUtil.writeResult(response, e.getMessage()); } else { SaFirewallStrategy.instance.checkFailHandle.run(e, saRequest, saResponse, null); } @@ -68,14 +69,5 @@ public class SaFirewallCheckFilterForJakartaServlet implements Filter { chain.doFilter(request, response); } - @Override - public void init(FilterConfig filterConfig) { - } - - @Override - public void destroy() { - } - - } diff --git a/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/filter/SaServletFilter.java b/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/filter/SaServletFilter.java index b7dfb153..7d3e6ff9 100644 --- a/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/filter/SaServletFilter.java +++ b/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/filter/SaServletFilter.java @@ -15,11 +15,11 @@ */ package cn.dev33.satoken.filter; -import cn.dev33.satoken.error.SaSpringBootErrorCode; import cn.dev33.satoken.exception.BackResultException; import cn.dev33.satoken.exception.SaTokenException; import cn.dev33.satoken.exception.StopMatchException; import cn.dev33.satoken.router.SaRouter; +import cn.dev33.satoken.servlet.util.SaJakartaServletOperateUtil; import cn.dev33.satoken.util.SaTokenConsts; import jakarta.servlet.*; import org.springframework.core.annotation.Order; @@ -30,7 +30,7 @@ import java.util.Arrays; import java.util.List; /** - * Jakarta-Servlet 全局鉴权过滤器 + * 全局鉴权过滤器 (基于 Jakarta-Servlet) *

* 默认优先级为 -100,尽量保证在其它过滤器之前执行 *

@@ -89,7 +89,7 @@ public class SaServletFilter implements SaFilter, Filter { * 异常处理函数:每次[认证函数]发生异常时执行此函数 */ public SaFilterErrorStrategy error = e -> { - throw new SaTokenException(e).setCode(SaSpringBootErrorCode.CODE_20105); + throw new SaTokenException(e); }; /** @@ -128,36 +128,19 @@ public class SaServletFilter implements SaFilter, Filter { SaRouter.match(includeList).notMatch(excludeList).check(r -> { auth.run(null); }); - - } catch (StopMatchException e) { - // StopMatchException 异常代表:停止匹配,进入Controller - - } catch (Throwable e) { - // 1. 获取异常处理策略结果 - String result = (e instanceof BackResultException) ? e.getMessage() : String.valueOf(error.run(e)); - - // 2. 写入输出流 - // 请注意此处默认 Content-Type 为 text/plain,如果需要返回 JSON 信息,需要在 return 前自行设置 Content-Type 为 application/json - // 例如:SaHolder.getResponse().setHeader("Content-Type", "application/json;charset=UTF-8"); - if(response.getContentType() == null) { - response.setContentType(SaTokenConsts.CONTENT_TYPE_TEXT_PLAIN); - } - response.getWriter().print(result); + } + catch (StopMatchException ignored) {} + catch (BackResultException e) { + SaJakartaServletOperateUtil.writeResult(response, e.getMessage()); + return; + } + catch (Throwable e) { + SaJakartaServletOperateUtil.writeResult(response, String.valueOf(error.run(e))); return; } // 执行 chain.doFilter(request, response); } - - @Override - public void init(FilterConfig filterConfig) { - } - - @Override - public void destroy() { - } - - } diff --git a/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/filter/SaTokenContextFilterForJakartaServlet.java b/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/filter/SaTokenContextFilterForJakartaServlet.java new file mode 100644 index 00000000..cd2ad401 --- /dev/null +++ b/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/filter/SaTokenContextFilterForJakartaServlet.java @@ -0,0 +1,46 @@ +/* + * 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.filter; + +import cn.dev33.satoken.servlet.util.SaTokenContextUtil; +import cn.dev33.satoken.util.SaTokenConsts; +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.core.annotation.Order; + +import java.io.IOException; + +/** + * SaTokenContext 上下文初始化过滤器 (基于 Jakarta-Servlet) + * + * @author click33 + * @since 1.42.0 + */ +@Order(SaTokenConsts.SA_TOKEN_CONTEXT_FILTER_ORDER) +public class SaTokenContextFilterForJakartaServlet implements Filter { + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + try { + SaTokenContextUtil.setContext((HttpServletRequest) request, (HttpServletResponse) response); + chain.doFilter(request, response); + } finally { + SaTokenContextUtil.clearContext(); + } + } + +} diff --git a/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextForSpringInJakartaServlet.java b/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextForSpringInJakartaServlet.java index 22a9a169..72a34f28 100644 --- a/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextForSpringInJakartaServlet.java +++ b/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextForSpringInJakartaServlet.java @@ -22,9 +22,10 @@ import cn.dev33.satoken.context.model.SaStorage; import cn.dev33.satoken.servlet.model.SaRequestForServlet; import cn.dev33.satoken.servlet.model.SaResponseForServlet; import cn.dev33.satoken.servlet.model.SaStorageForServlet; -import cn.dev33.satoken.spring.pathmatch.SaPathPatternParserUtil; /** + *

此为低版本(<1.42.0) 的上下文处理方案,基于 Spring 内部工具类 RequestContextHolder 读写上下文,仅做留档,如无必要请勿使用

+ * * Sa-Token 上下文处理器 [ SpringBoot3 Jakarta Servlet 版 ],在 SpringBoot3 中使用 Sa-Token 时,必须注入此实现类,否则会出现上下文无效异常 * * @author click33 @@ -55,14 +56,6 @@ public class SaTokenContextForSpringInJakartaServlet implements SaTokenContext { public SaStorage getStorage() { return new SaStorageForServlet(SpringMVCUtil.getRequest()); } - - /** - * 判断:指定路由匹配符是否可以匹配成功指定路径 - */ - @Override - public boolean matchPath(String pattern, String path) { - return SaPathPatternParserUtil.match(pattern, path); - } /** * 判断:在本次请求中,此上下文是否可用。 diff --git a/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextRegister.java b/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextRegister.java index 296d3154..48cc43d9 100644 --- a/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextRegister.java +++ b/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/spring/SaTokenContextRegister.java @@ -15,8 +15,10 @@ */ package cn.dev33.satoken.spring; -import cn.dev33.satoken.context.SaTokenContext; import cn.dev33.satoken.filter.SaFirewallCheckFilterForJakartaServlet; +import cn.dev33.satoken.filter.SaTokenContextFilterForJakartaServlet; +import cn.dev33.satoken.spring.pathmatch.SaPathPatternParserUtil; +import cn.dev33.satoken.strategy.SaStrategy; import org.springframework.context.annotation.Bean; /** @@ -27,18 +29,25 @@ import org.springframework.context.annotation.Bean; */ public class SaTokenContextRegister { - /** - * 获取上下文处理器组件 (SpringBoot3 Jakarta Servlet 版) - * - * @return / - */ - @Bean - public SaTokenContext getSaTokenContextForSpringInJakartaServlet() { - return new SaTokenContextForSpringInJakartaServlet(); + public SaTokenContextRegister() { + // 重写路由匹配算法 + SaStrategy.instance.routeMatcher = (pattern, path) -> { + return SaPathPatternParserUtil.match(pattern, path); + }; } /** - * 请求 path 校验过滤器 + * 上下文过滤器 + * + * @return / + */ + @Bean + public SaTokenContextFilterForJakartaServlet saTokenContextFilterForServlet() { + return new SaTokenContextFilterForJakartaServlet(); + } + + /** + * 防火墙过滤器 * * @return / */ diff --git a/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/spring/SpringMVCUtil.java b/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/spring/SpringMVCUtil.java index 5eacab05..4f52cdca 100644 --- a/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/spring/SpringMVCUtil.java +++ b/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/spring/SpringMVCUtil.java @@ -15,13 +15,11 @@ */ package cn.dev33.satoken.spring; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -import cn.dev33.satoken.error.SaSpringBootErrorCode; import cn.dev33.satoken.exception.NotWebContextException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; /** * SpringMVC 相关操作工具类,快速获取当前会话的 HttpServletRequest、HttpServletResponse 对象 @@ -41,7 +39,7 @@ public class SpringMVCUtil { public static HttpServletRequest getRequest() { ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if(servletRequestAttributes == null) { - throw new NotWebContextException("非 web 上下文无法获取 HttpServletRequest").setCode(SaSpringBootErrorCode.CODE_20101); + throw new NotWebContextException("非 web 上下文无法获取 HttpServletRequest"); } return servletRequestAttributes.getRequest(); } @@ -53,7 +51,7 @@ public class SpringMVCUtil { public static HttpServletResponse getResponse() { ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if(servletRequestAttributes == null) { - throw new NotWebContextException("非 web 上下文无法获取 HttpServletRequest").setCode(SaSpringBootErrorCode.CODE_20101); + throw new NotWebContextException("非 web 上下文无法获取 HttpServletRequest"); } return servletRequestAttributes.getResponse(); }