mirror of
https://gitee.com/dromara/sa-token.git
synced 2025-08-20 00:44:30 +08:00
修复路由拦截鉴权可被绕过的问题 fix #515
This commit is contained in:
parent
f2416a6175
commit
954efeb732
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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 的算法
|
||||
*
|
||||
* <p> 如果属于无效请求 path,则抛出异常 RequestPathInvalidException </p>
|
||||
*
|
||||
* @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);
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -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 校验不通过时处理方案的算法,自定义示例:
|
||||
* <pre>
|
||||
* SaStrategy.instance.requestPathInvalidHandle = (e, extArg1, extArg2) -> {
|
||||
* // 自定义处理逻辑 ...
|
||||
* };
|
||||
* </pre>
|
||||
*/
|
||||
public SaRequestPathInvalidHandleFunction requestPathInvalidHandle = null;
|
||||
|
||||
|
||||
// ----------------------- 重写策略 set连缀风格
|
||||
|
||||
|
||||
@ -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";
|
||||
|
||||
|
||||
|
||||
|
||||
// =================== 废弃 ===================
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ sa-token:
|
||||
# 是否输出操作日志
|
||||
is-log: true
|
||||
|
||||
spring:
|
||||
spring:
|
||||
data:
|
||||
# redis配置
|
||||
redis:
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.5.14</version>
|
||||
<!--<version>2.3.0.RELEASE</version>-->
|
||||
<!-- <version>1.5.9.RELEASE</version> -->
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
@ -23,5 +23,5 @@ public class NotFoundHandle implements ErrorController {
|
||||
response.setStatus(200);
|
||||
return SaResult.get(404, "not found", null);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package com.pj.test;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.spring.SpringMVCUtil;
|
||||
import cn.dev33.satoken.stp.SaLoginConfig;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
@ -41,4 +43,13 @@ public class TestController {
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/getRequestPath
|
||||
@RequestMapping("getRequestPath")
|
||||
public SaResult getRequestPath() {
|
||||
System.out.println("------------ 测试访问路径获取 ");
|
||||
System.out.println("SpringMVCUtil.getRequest().getRequestURI() " + SpringMVCUtil.getRequest().getRequestURI());
|
||||
System.out.println("SaHolder.getRequest().getRequestPath() " + SaHolder.getRequest().getRequestPath());
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ sa-token:
|
||||
# 是否输出操作日志
|
||||
is-log: true
|
||||
|
||||
spring:
|
||||
spring:
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
package com.pj;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
|
||||
/**
|
||||
* Sa-Token整合webflux 示例 (springboot3)
|
||||
*
|
||||
|
||||
@ -19,7 +19,7 @@ sa-token:
|
||||
# 是否输出操作日志
|
||||
is-log: true
|
||||
|
||||
spring:
|
||||
spring:
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
|
||||
@ -19,7 +19,7 @@ sa-token:
|
||||
# 是否输出操作日志
|
||||
is-log: true
|
||||
|
||||
spring:
|
||||
spring:
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
package cn.dev33.satoken.servlet.model;
|
||||
|
||||
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;
|
||||
@ -124,7 +125,7 @@ public class SaRequestForServlet implements SaRequest {
|
||||
*/
|
||||
@Override
|
||||
public String getRequestPath() {
|
||||
return request.getServletPath();
|
||||
return ApplicationInfo.cutPathPrefix(request.getRequestURI());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2020-2099 sa-token.cc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.reactor.filter;
|
||||
|
||||
import cn.dev33.satoken.exception.RequestPathInvalidException;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 校验请求 path 是否合法
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.37.0
|
||||
*/
|
||||
@Order(SaTokenConsts.PATH_CHECK_FILTER_ORDER)
|
||||
public class SaPathCheckFilterForReactor implements WebFilter {
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
|
||||
// 校验本次请求 path 是否合法
|
||||
try {
|
||||
SaStrategy.instance.checkRequestPath.run(exchange.getRequest().getPath().toString(), exchange, null);
|
||||
} catch (RequestPathInvalidException e) {
|
||||
if(SaStrategy.instance.requestPathInvalidHandle == 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())));
|
||||
} else {
|
||||
SaStrategy.instance.requestPathInvalidHandle.run(e, exchange, null);
|
||||
}
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
// 向下执行
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
}
|
||||
@ -153,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())));
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ package cn.dev33.satoken.reactor.model;
|
||||
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.application.ApplicationInfo;
|
||||
import cn.dev33.satoken.context.model.SaRequest;
|
||||
import cn.dev33.satoken.reactor.context.SaReactorHolder;
|
||||
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
|
||||
@ -112,7 +113,7 @@ public class SaRequestForReactor implements SaRequest {
|
||||
*/
|
||||
@Override
|
||||
public String getRequestPath() {
|
||||
return request.getURI().getPath();
|
||||
return ApplicationInfo.cutPathPrefix(request.getPath().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package cn.dev33.satoken.reactor.spring;
|
||||
|
||||
import cn.dev33.satoken.reactor.filter.SaPathCheckFilterForReactor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import cn.dev33.satoken.context.SaTokenContext;
|
||||
@ -37,4 +38,14 @@ public class SaTokenContextRegister {
|
||||
return new SaTokenContextForSpringReactor();
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求 path 校验过滤器
|
||||
*
|
||||
* @return /
|
||||
*/
|
||||
@Bean
|
||||
public SaPathCheckFilterForReactor saPathCheckFilterForReactor() {
|
||||
return new SaPathCheckFilterForReactor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2020-2099 sa-token.cc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.reactor.filter;
|
||||
|
||||
import cn.dev33.satoken.exception.RequestPathInvalidException;
|
||||
import cn.dev33.satoken.strategy.SaStrategy;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 校验请求 path 是否合法
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.37.0
|
||||
*/
|
||||
@Order(SaTokenConsts.PATH_CHECK_FILTER_ORDER)
|
||||
public class SaPathCheckFilterForReactor implements WebFilter {
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
|
||||
// 校验本次请求 path 是否合法
|
||||
try {
|
||||
SaStrategy.instance.checkRequestPath.run(exchange.getRequest().getPath().toString(), exchange, null);
|
||||
} catch (RequestPathInvalidException e) {
|
||||
if(SaStrategy.instance.requestPathInvalidHandle == 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())));
|
||||
} else {
|
||||
SaStrategy.instance.requestPathInvalidHandle.run(e, exchange, null);
|
||||
}
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
// 向下执行
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
}
|
||||
@ -15,19 +15,10 @@
|
||||
*/
|
||||
package cn.dev33.satoken.reactor.filter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import cn.dev33.satoken.filter.SaFilter;
|
||||
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 cn.dev33.satoken.exception.BackResultException;
|
||||
import cn.dev33.satoken.exception.SaTokenException;
|
||||
import cn.dev33.satoken.exception.StopMatchException;
|
||||
import cn.dev33.satoken.filter.SaFilter;
|
||||
import cn.dev33.satoken.filter.SaFilterAuthStrategy;
|
||||
import cn.dev33.satoken.filter.SaFilterErrorStrategy;
|
||||
import cn.dev33.satoken.reactor.context.SaReactorHolder;
|
||||
@ -35,8 +26,16 @@ import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
|
||||
import cn.dev33.satoken.reactor.error.SaReactorSpringBootErrorCode;
|
||||
import cn.dev33.satoken.router.SaRouter;
|
||||
import cn.dev33.satoken.util.SaTokenConsts;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Reactor 全局鉴权过滤器
|
||||
* <p>
|
||||
@ -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())));
|
||||
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user