From 954efeb73277f924f836da2a25322ea35ee1bfa3 Mon Sep 17 00:00:00 2001 From: click33 <2393584716@qq.com> Date: Mon, 16 Oct 2023 16:02:19 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=B7=AF=E7=94=B1=E6=8B=A6?= =?UTF-8?q?=E6=88=AA=E9=89=B4=E6=9D=83=E5=8F=AF=E8=A2=AB=E7=BB=95=E8=BF=87?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98=20fix=20#515?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../satoken/application/ApplicationInfo.java | 45 ++++++++++++ .../RequestPathInvalidException.java | 46 +++++++++++++ .../strategy/SaCheckRequestPathFunction.java | 39 +++++++++++ .../SaRequestPathInvalidHandleFunction.java | 37 ++++++++++ .../cn/dev33/satoken/strategy/SaStrategy.java | 44 ++++++++++++ .../cn/dev33/satoken/util/SaTokenConsts.java | 22 ++++++ .../main/java/com/pj/test/TestController.java | 14 +++- .../src/main/resources/application.yml | 2 +- sa-token-demo/sa-token-demo-test/pom.xml | 1 + .../java/com/pj/current/NotFoundHandle.java | 2 +- .../main/java/com/pj/test/TestController.java | 11 +++ .../src/main/resources/application.yml | 2 +- .../SaTokenWebfluxSpringboot3Application.java | 3 +- .../src/main/resources/application.yml | 2 +- .../src/main/resources/application.yml | 2 +- .../servlet/model/SaRequestForServlet.java | 3 +- .../filter/SaPathCheckFilterForReactor.java | 56 +++++++++++++++ .../reactor/filter/SaReactorFilter.java | 4 +- .../reactor/model/SaRequestForReactor.java | 3 +- .../spring/SaTokenContextRegister.java | 11 +++ .../filter/SaPathCheckFilterForReactor.java | 56 +++++++++++++++ .../reactor/filter/SaReactorFilter.java | 23 +++---- .../reactor/model/SaRequestForReactor.java | 12 ++-- .../spring/SaTokenContextRegister.java | 14 +++- .../servlet/model/SaRequestForServlet.java | 24 +++---- .../dev33/satoken/spring/SaBeanRegister.java | 12 +++- .../path/ApplicationContextPathLoading.java | 68 +++++++++++++++++++ .../filter/SaPathCheckFilterForServlet.java | 68 +++++++++++++++++++ .../dev33/satoken/filter/SaServletFilter.java | 23 +++---- .../spring/SaTokenContextRegister.java | 14 +++- .../SaPathCheckFilterForJakartaServlet.java | 68 +++++++++++++++++++ .../dev33/satoken/filter/SaServletFilter.java | 22 +++--- .../spring/SaTokenContextRegister.java | 14 +++- 33 files changed, 688 insertions(+), 79 deletions(-) create mode 100644 sa-token-core/src/main/java/cn/dev33/satoken/application/ApplicationInfo.java create mode 100644 sa-token-core/src/main/java/cn/dev33/satoken/exception/RequestPathInvalidException.java create mode 100644 sa-token-core/src/main/java/cn/dev33/satoken/fun/strategy/SaCheckRequestPathFunction.java create mode 100644 sa-token-core/src/main/java/cn/dev33/satoken/fun/strategy/SaRequestPathInvalidHandleFunction.java create mode 100644 sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaPathCheckFilterForReactor.java create mode 100644 sa-token-starter/sa-token-reactor-spring-boot3-starter/src/main/java/cn/dev33/satoken/reactor/filter/SaPathCheckFilterForReactor.java create mode 100644 sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/context/path/ApplicationContextPathLoading.java create mode 100644 sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/filter/SaPathCheckFilterForServlet.java create mode 100644 sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/filter/SaPathCheckFilterForJakartaServlet.java diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/application/ApplicationInfo.java b/sa-token-core/src/main/java/cn/dev33/satoken/application/ApplicationInfo.java new file mode 100644 index 00000000..93792d03 --- /dev/null +++ b/sa-token-core/src/main/java/cn/dev33/satoken/application/ApplicationInfo.java @@ -0,0 +1,45 @@ +/* + * 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.application; + +import cn.dev33.satoken.util.SaFoxUtil; + +/** + * 应用全局信息 + * + * @author click33 + * @since 1.31.0 + */ +public class ApplicationInfo { + + /** + * 应用前缀 + */ + public static String routePrefix; + + /** + * 为指定 path 裁剪掉 routePrefix 前缀 + * @param path 指定 path + * @return / + */ + public static String cutPathPrefix(String path) { + if(! SaFoxUtil.isEmpty(routePrefix) && ! routePrefix.equals("/") && path.startsWith(routePrefix)){ + path = path.substring(routePrefix.length()); + } + return path; + } + +} \ No newline at end of file diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/exception/RequestPathInvalidException.java b/sa-token-core/src/main/java/cn/dev33/satoken/exception/RequestPathInvalidException.java new file mode 100644 index 00000000..e09cf9e0 --- /dev/null +++ b/sa-token-core/src/main/java/cn/dev33/satoken/exception/RequestPathInvalidException.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.exception; + +/** + * 一个异常:代表请求 path 无效或非法 + * + * @author click33 + * @since 1.37.0 + */ +public class RequestPathInvalidException extends SaTokenException { + + /** + * 序列化版本号 + */ + private static final long serialVersionUID = 8243974276159004739L; + + /** 具体无效的 path */ + private final String path; + + /** + * @return 具体无效的 path + */ + public String getPath() { + return path; + } + + public RequestPathInvalidException(String message, String path) { + super(message); + this.path = path; + } + +} diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/fun/strategy/SaCheckRequestPathFunction.java b/sa-token-core/src/main/java/cn/dev33/satoken/fun/strategy/SaCheckRequestPathFunction.java new file mode 100644 index 00000000..dcca032f --- /dev/null +++ b/sa-token-core/src/main/java/cn/dev33/satoken/fun/strategy/SaCheckRequestPathFunction.java @@ -0,0 +1,39 @@ +/* + * 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.fun.strategy; + +import cn.dev33.satoken.exception.RequestPathInvalidException; + +/** + * 函数式接口:校验请求 path 的算法 + * + *
如果属于无效请求 path,则抛出异常 RequestPathInvalidException
+ * + * @author click33 + * @since 1.37.0 + */ +@FunctionalInterface +public interface SaCheckRequestPathFunction { + + /** + * 执行函数 + * @param path 请求 path + * @param extArg1 扩展参数1 + * @param extArg2 扩展参数2 + */ + void run(String path, Object extArg1, Object extArg2); + +} \ No newline at end of file diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/fun/strategy/SaRequestPathInvalidHandleFunction.java b/sa-token-core/src/main/java/cn/dev33/satoken/fun/strategy/SaRequestPathInvalidHandleFunction.java new file mode 100644 index 00000000..d3880465 --- /dev/null +++ b/sa-token-core/src/main/java/cn/dev33/satoken/fun/strategy/SaRequestPathInvalidHandleFunction.java @@ -0,0 +1,37 @@ +/* + * Copyright 2020-2099 sa-token.cc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package cn.dev33.satoken.fun.strategy; + +import cn.dev33.satoken.exception.RequestPathInvalidException; + +/** + * 函数式接口:当请求 path 校验不通过时处理方案的算法 + * + * @author click33 + * @since 1.37.0 + */ +@FunctionalInterface +public interface SaRequestPathInvalidHandleFunction { + + /** + * 执行函数 + * @param e 请求 path 无效的异常对象 + * @param extArg1 扩展参数1 + * @param extArg2 扩展参数2 + */ + void run(RequestPathInvalidException e, Object extArg1, Object extArg2); + +} \ No newline at end of file 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 d68a3ad3..06115533 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 @@ -18,6 +18,7 @@ package cn.dev33.satoken.strategy; import cn.dev33.satoken.SaManager; import cn.dev33.satoken.annotation.*; import cn.dev33.satoken.basic.SaBasicUtil; +import cn.dev33.satoken.exception.RequestPathInvalidException; import cn.dev33.satoken.exception.SaTokenException; import cn.dev33.satoken.fun.strategy.*; import cn.dev33.satoken.session.SaSession; @@ -329,6 +330,49 @@ public final class SaStrategy { return new StpLogic(loginType); }; + /** + * 请求 path 不允许出现的字符 + */ + public static String[] INVALID_CHARACTER = { + "//", "\\", + "%2e", "%2E", // . + "%2f", "%2F", // / + "%5c", "%5C", // \ + "%25" // 空格 + }; + + /** + * 校验请求 path 的算法 + */ + public SaCheckRequestPathFunction checkRequestPath = (requestPath, extArg1, extArg2) -> { + + // 不允许为null + if(requestPath == null) { + throw new RequestPathInvalidException("非法请求:null", null); + } + // 不允许包含非法字符 + for (String item : INVALID_CHARACTER) { + if (requestPath.contains(item)) { + throw new RequestPathInvalidException("非法请求:" + requestPath, requestPath); + } + } + // 不允许出现跨目录 + if(requestPath.contains("/.") || requestPath.contains("\\.")) { + throw new RequestPathInvalidException("非法请求:" + requestPath, requestPath); + } + }; + + + /** + * 当请求 path 校验不通过时处理方案的算法,自定义示例: + *
+ * SaStrategy.instance.requestPathInvalidHandle = (e, extArg1, extArg2) -> {
+ * // 自定义处理逻辑 ...
+ * };
+ *
+ */
+ public SaRequestPathInvalidHandleFunction requestPathInvalidHandle = null;
+
// ----------------------- 重写策略 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 c2dcda2e..921d22ce 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
@@ -186,6 +186,28 @@ public class SaTokenConsts {
*/
public static final int ASSEMBLY_ORDER = -100;
+ /**
+ * 请求 path 校验过滤器的注册顺序
+ */
+ public static final int PATH_CHECK_FILTER_ORDER = -1000;
+
+ /**
+ * Content-Type key
+ */
+ public static final String CONTENT_TYPE_KEY = "Content-Type";
+
+ /**
+ * Content-Type text/plain; charset=utf-8
+ */
+ public static final String CONTENT_TYPE_TEXT_PLAIN = "text/plain; charset=utf-8";
+
+ /**
+ * Content-Type application/json;charset=UTF-8
+ */
+ public static final String CONTENT_TYPE_APPLICATION_JSON = "application/json;charset=UTF-8";
+
+
+
// =================== 废弃 ===================
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 065fa706..fc6d1844 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,10 +1,11 @@
package com.pj.test;
+import cn.dev33.satoken.context.SaHolder;
+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;
-import cn.dev33.satoken.util.SaResult;
-
/**
* 测试专用Controller
* @author click33
@@ -27,4 +28,13 @@ public class TestController {
return SaResult.ok();
}
+ // 测试 浏览器访问: http://localhost:8081/test/getRequestPath
+ @RequestMapping("getRequestPath")
+ public SaResult getRequestPath() {
+ System.out.println("-------------- 测试请求 path 获取");
+ System.out.println("request.getRequestURI() " + SpringMVCUtil.getRequest().getRequestURI());
+ System.out.println("saRequest.getRequestPath() " + SaHolder.getRequest().getRequestPath());
+ return SaResult.ok();
+ }
+
}
diff --git a/sa-token-demo/sa-token-demo-springboot3-redis/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-springboot3-redis/src/main/resources/application.yml
index 1b1934e0..d872c2b8 100644
--- a/sa-token-demo/sa-token-demo-springboot3-redis/src/main/resources/application.yml
+++ b/sa-token-demo/sa-token-demo-springboot3-redis/src/main/resources/application.yml
@@ -19,7 +19,7 @@ sa-token:
# 是否输出操作日志
is-log: true
-spring:
+spring:
data:
# redis配置
redis:
diff --git a/sa-token-demo/sa-token-demo-test/pom.xml b/sa-token-demo/sa-token-demo-test/pom.xml
index b614b20f..019ebfa7 100644
--- a/sa-token-demo/sa-token-demo-test/pom.xml
+++ b/sa-token-demo/sa-token-demo-test/pom.xml
@@ -11,6 +11,7 @@
@@ -154,8 +153,8 @@ public class SaReactorFilter implements SaFilter, WebFilter { // 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("Content-Type") == null) { - exchange.getResponse().getHeaders().set("Content-Type", "text/plain; 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/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 4a85b97b..c8a5c588 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 @@ -16,16 +16,16 @@ package cn.dev33.satoken.reactor.model; -import org.springframework.http.HttpCookie; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.web.server.ServerWebExchange; -import org.springframework.web.server.WebFilterChain; - import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.application.ApplicationInfo; import cn.dev33.satoken.context.model.SaRequest; import cn.dev33.satoken.reactor.context.SaReactorHolder; import cn.dev33.satoken.reactor.context.SaReactorSyncHolder; import cn.dev33.satoken.util.SaFoxUtil; +import org.springframework.http.HttpCookie; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilterChain; import java.util.ArrayList; import java.util.List; @@ -113,7 +113,7 @@ public class SaRequestForReactor implements SaRequest { */ @Override public String getRequestPath() { - return request.getURI().getPath(); + return ApplicationInfo.cutPathPrefix(request.getPath().toString()); } /** 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 059ae1a5..1644f11d 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,9 +15,9 @@ */ package cn.dev33.satoken.reactor.spring; -import org.springframework.context.annotation.Bean; - import cn.dev33.satoken.context.SaTokenContext; +import cn.dev33.satoken.reactor.filter.SaPathCheckFilterForReactor; +import org.springframework.context.annotation.Bean; /** * 注册 Sa-Token 所需要的 Bean @@ -37,4 +37,14 @@ public class SaTokenContextRegister { return new SaTokenContextForSpringReactor(); } + /** + * 请求 path 校验过滤器 + * + * @return / + */ + @Bean + public SaPathCheckFilterForReactor saPathCheckFilterForReactor() { + return new SaPathCheckFilterForReactor(); + } + } 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 d3f0d2e7..d1228b51 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 @@ -15,19 +15,19 @@ */ package cn.dev33.satoken.servlet.model; -import java.io.IOException; -import java.util.*; +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.application.ApplicationInfo; +import cn.dev33.satoken.context.model.SaRequest; +import cn.dev33.satoken.exception.SaTokenException; +import cn.dev33.satoken.servlet.error.SaServletErrorCode; +import cn.dev33.satoken.util.SaFoxUtil; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - -import cn.dev33.satoken.SaManager; -import cn.dev33.satoken.context.model.SaRequest; -import cn.dev33.satoken.exception.SaTokenException; -import cn.dev33.satoken.servlet.error.SaServletErrorCode; -import cn.dev33.satoken.util.SaFoxUtil; +import java.io.IOException; +import java.util.*; /** * 对 SaRequest 包装类的实现(Servlet 版) @@ -41,15 +41,15 @@ public class SaRequestForServlet implements SaRequest { * 底层Request对象 */ protected HttpServletRequest request; - + /** * 实例化 - * @param request request对象 + * @param request request对象 */ public SaRequestForServlet(HttpServletRequest request) { this.request = request; } - + /** * 获取底层源对象 */ @@ -125,7 +125,7 @@ public class SaRequestForServlet implements SaRequest { */ @Override public String getRequestPath() { - return request.getServletPath(); + return ApplicationInfo.cutPathPrefix(request.getRequestURI()); } /** diff --git a/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/SaBeanRegister.java b/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/SaBeanRegister.java index 1c96d687..57c28357 100644 --- a/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/SaBeanRegister.java +++ b/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/SaBeanRegister.java @@ -15,6 +15,7 @@ */ package cn.dev33.satoken.spring; +import cn.dev33.satoken.spring.context.path.ApplicationContextPathLoading; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -50,5 +51,14 @@ public class SaBeanRegister { public SaJsonTemplate getSaJsonTemplateForJackson() { return new SaJsonTemplateForJackson(); } - + + /** + * 应用上下文路径加载器 + * @return / + */ + @Bean + public ApplicationContextPathLoading getApplicationContextPathLoading() { + return new ApplicationContextPathLoading(); + } + } diff --git a/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/context/path/ApplicationContextPathLoading.java b/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/context/path/ApplicationContextPathLoading.java new file mode 100644 index 00000000..9ce85586 --- /dev/null +++ b/sa-token-starter/sa-token-spring-boot-autoconfig/src/main/java/cn/dev33/satoken/spring/context/path/ApplicationContextPathLoading.java @@ -0,0 +1,68 @@ +/* + * 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.context.path; + +import cn.dev33.satoken.application.ApplicationInfo; +import cn.dev33.satoken.util.SaFoxUtil; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; + +/** + * 应用上下文路径加载器 + * + * @author click33 + * @since 1.37.0 + */ +public class ApplicationContextPathLoading implements ApplicationRunner { + + @Value("${server.servlet.context-path:}") + String contextPath; + + @Value("${spring.mvc.servlet.path:}") + String servletPath; + + @Override + public void run(ApplicationArguments args) throws Exception { + + String routePrefix = ""; + + if(SaFoxUtil.isNotEmpty(contextPath)) { + if(! contextPath.startsWith("/")){ + contextPath = "/" + contextPath; + } + if (contextPath.endsWith("/")) { + contextPath = contextPath.substring(0, contextPath.length() - 1); + } + routePrefix += contextPath; + } + + if(SaFoxUtil.isNotEmpty(servletPath)) { + if(! servletPath.startsWith("/")){ + servletPath = "/" + servletPath; + } + if (servletPath.endsWith("/")) { + servletPath = servletPath.substring(0, servletPath.length() - 1); + } + routePrefix += servletPath; + } + + if(SaFoxUtil.isNotEmpty(routePrefix) && ! routePrefix.equals("/") ){ + ApplicationInfo.routePrefix = routePrefix; + } + } + +} \ No newline at end of file diff --git a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/filter/SaPathCheckFilterForServlet.java b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/filter/SaPathCheckFilterForServlet.java new file mode 100644 index 00000000..8334f69f --- /dev/null +++ b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/filter/SaPathCheckFilterForServlet.java @@ -0,0 +1,68 @@ +/* + * 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.exception.RequestPathInvalidException; +import cn.dev33.satoken.strategy.SaStrategy; +import cn.dev33.satoken.util.SaTokenConsts; +import org.springframework.core.annotation.Order; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +/** + * 校验请求 path 是否合法 + * + * @author click33 + * @since 1.37.0 + */ +@Order(SaTokenConsts.PATH_CHECK_FILTER_ORDER) +public class SaPathCheckFilterForServlet implements Filter { + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + + // 校验本次请求 path 是否合法 + try { + HttpServletRequest req = (HttpServletRequest) request; + SaStrategy.instance.checkRequestPath.run(req.getRequestURI(), request, response); + } catch (RequestPathInvalidException e) { + if(SaStrategy.instance.requestPathInvalidHandle == null) { + response.setContentType("text/plain; charset=utf-8"); + response.getWriter().print(e.getMessage()); + response.getWriter().flush(); + } else { + SaStrategy.instance.requestPathInvalidHandle.run(e, request, response); + } + return; + } + + // 向下执行 + 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 7cc77e38..8fd3eb9f 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,26 +15,19 @@ */ package cn.dev33.satoken.filter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; - -import org.springframework.core.annotation.Order; - 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.util.SaTokenConsts; +import org.springframework.core.annotation.Order; + +import javax.servlet.*; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; /** * Servlet 全局鉴权过滤器 @@ -147,7 +140,7 @@ public class SaServletFilter implements SaFilter, Filter { // 请注意此处默认 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("text/plain; charset=utf-8"); + response.setContentType(SaTokenConsts.CONTENT_TYPE_TEXT_PLAIN); } response.getWriter().print(result); return; 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 c380827c..fa24e0d9 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,9 +15,9 @@ */ package cn.dev33.satoken.spring; -import org.springframework.context.annotation.Bean; - import cn.dev33.satoken.context.SaTokenContext; +import cn.dev33.satoken.filter.SaPathCheckFilterForServlet; +import org.springframework.context.annotation.Bean; /** * 注册 Sa-Token 框架所需要的 Bean @@ -37,4 +37,14 @@ public class SaTokenContextRegister { return new SaTokenContextForSpring(); } + /** + * 请求 path 校验过滤器 + * + * @return / + */ + @Bean + public SaPathCheckFilterForServlet saPathCheckFilterForServlet() { + return new SaPathCheckFilterForServlet(); + } + } diff --git a/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/filter/SaPathCheckFilterForJakartaServlet.java b/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/filter/SaPathCheckFilterForJakartaServlet.java new file mode 100644 index 00000000..828adb7c --- /dev/null +++ b/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/filter/SaPathCheckFilterForJakartaServlet.java @@ -0,0 +1,68 @@ +/* + * 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.exception.RequestPathInvalidException; +import cn.dev33.satoken.strategy.SaStrategy; +import cn.dev33.satoken.util.SaTokenConsts; +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.core.annotation.Order; + +import java.io.IOException; + +/** + * 校验请求 path 是否合法 + * + * @author click33 + * @since 1.37.0 + */ +@Order(SaTokenConsts.PATH_CHECK_FILTER_ORDER) +public class SaPathCheckFilterForJakartaServlet implements Filter { + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + + // 校验本次请求 path 是否合法 + try { + HttpServletRequest req = (HttpServletRequest) request; + SaStrategy.instance.checkRequestPath.run(req.getRequestURI(), request, response); + } catch (RequestPathInvalidException e) { + if(SaStrategy.instance.requestPathInvalidHandle == null) { + response.setContentType("text/plain; charset=utf-8"); + response.getWriter().print(e.getMessage()); + response.getWriter().flush(); + } else { + SaStrategy.instance.requestPathInvalidHandle.run(e, request, response); + } + 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/SaServletFilter.java b/sa-token-starter/sa-token-spring-boot3-starter/src/main/java/cn/dev33/satoken/filter/SaServletFilter.java index bbfcc4f3..e634a097 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,25 +15,19 @@ */ package cn.dev33.satoken.filter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.springframework.core.annotation.Order; - 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.util.SaTokenConsts; -import jakarta.servlet.Filter; -import jakarta.servlet.FilterChain; -import jakarta.servlet.FilterConfig; -import jakarta.servlet.ServletException; -import jakarta.servlet.ServletRequest; -import jakarta.servlet.ServletResponse; +import jakarta.servlet.*; +import org.springframework.core.annotation.Order; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; /** * Servlet 全局鉴权过滤器 @@ -146,7 +140,7 @@ public class SaServletFilter implements SaFilter, Filter { // 请注意此处默认 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("text/plain; charset=utf-8"); + response.setContentType(SaTokenConsts.CONTENT_TYPE_TEXT_PLAIN); } response.getWriter().print(result); return; 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 0f881a0f..d8ab7b98 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,9 +15,9 @@ */ package cn.dev33.satoken.spring; -import org.springframework.context.annotation.Bean; - import cn.dev33.satoken.context.SaTokenContext; +import cn.dev33.satoken.filter.SaPathCheckFilterForJakartaServlet; +import org.springframework.context.annotation.Bean; /** * 注册 Sa-Token 框架所需要的 Bean @@ -37,4 +37,14 @@ public class SaTokenContextRegister { return new SaTokenContextForSpringInJakartaServlet(); } + /** + * 请求 path 校验过滤器 + * + * @return / + */ + @Bean + public SaPathCheckFilterForJakartaServlet saPathCheckFilterForJakartaServlet() { + return new SaPathCheckFilterForJakartaServlet(); + } + }