修复 SaRouter.match 匹配规则与 Spring 默认不一致导致的越权漏洞

This commit is contained in:
click33 2023-09-21 05:06:40 +08:00
parent 5a6e8bd463
commit cd14651dcc
8 changed files with 137 additions and 7 deletions

View File

@ -16,7 +16,7 @@
package cn.dev33.satoken.reactor.spring;
import cn.dev33.satoken.context.SaTokenContextForThreadLocal;
import cn.dev33.satoken.spring.SaPathMatcherHolder;
import cn.dev33.satoken.spring.SaPathPatternParserUtil;
/**
* Sa-Token 上下文处理器 [ Spring Reactor 版本实现 ] 基于 SaTokenContextForThreadLocal 定制
@ -31,7 +31,7 @@ public class SaTokenContextForSpringReactor extends SaTokenContextForThreadLocal
*/
@Override
public boolean matchPath(String pattern, String path) {
return SaPathMatcherHolder.getPathMatcher().match(pattern, path);
return SaPathPatternParserUtil.match(pattern, path);
}
}

View File

@ -16,7 +16,7 @@
package cn.dev33.satoken.reactor.spring;
import cn.dev33.satoken.context.SaTokenContextForThreadLocal;
import cn.dev33.satoken.spring.SaPathMatcherHolder;
import cn.dev33.satoken.spring.SaPathPatternParserUtil;
/**
* Sa-Token 上下文处理器 [ Spring Reactor 版本实现 ] 基于 SaTokenContextForThreadLocal 定制
@ -31,7 +31,7 @@ public class SaTokenContextForSpringReactor extends SaTokenContextForThreadLocal
*/
@Override
public boolean matchPath(String pattern, String path) {
return SaPathMatcherHolder.getPathMatcher().match(pattern, path);
return SaPathPatternParserUtil.match(pattern, path);
}
}

View File

@ -24,6 +24,12 @@
<artifactId>spring-boot-starter</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.7</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>

View File

@ -19,7 +19,7 @@ import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
/**
* 持有 PathMatcher 全局引用方便快捷的调用 PathMatcher 相关方法
* 路由匹配工具类持有 PathMatcher 全局引用方便快捷的调用 PathMatcher 相关方法
*
* @author click33
* @since 1.34.0

View File

@ -0,0 +1,53 @@
/*
* 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 org.springframework.http.server.PathContainer;
import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPatternParser;
/**
* 路由匹配工具类使用 PathPatternParser 模式匹配
*
* @author click33
* @since 1.35.1
*/
public class SaPathPatternParserUtil {
private SaPathPatternParserUtil() {
}
/**
* 判断指定路由匹配符是否可以匹配成功指定路径
* @param pattern 路由匹配符
* @param path 要匹配的路径
* @return 是否匹配成功
*/
public static boolean match(String pattern, String path) {
PathPattern pathPattern = PathPatternParser.defaultInstance.parse(pattern);
PathContainer pathContainer = PathContainer.parsePath(path);
return pathPattern.matches(pathContainer);
}
/*
表现
springboot 2.x SpringMVC match("/test/test", "/test/test/") // true
springboot 2.x WebFlux match("/test/test", "/test/test/") // true
springboot 3.x SpringMVC match("/test/test", "/test/test/") // false
springboot 3.x WebFlux match("/test/test", "/test/test/") // false
*/
}

View File

@ -0,0 +1,71 @@
/*
* 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.exception.SaTokenException;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 路由匹配工具类
*
* @author click33
* @since 1.35.1
*/
public class SaPatternsRequestConditionHolder {
private SaPatternsRequestConditionHolder() {
}
public static PatternsRequestCondition patternsRequestCondition;
public static Method matcherMethod;
static {
try {
patternsRequestCondition = new PatternsRequestCondition();
matcherMethod = PatternsRequestCondition.class.getDeclaredMethod("getMatchingPattern", String.class, String.class);
matcherMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new SaTokenException("路由匹配器初始化失败", e);
}
}
/**
* 判断指定路由匹配符是否可以匹配成功指定路径
* @param pattern 路由匹配符
* @param lookupPath 要匹配的路径
* @return 是否匹配成功
*/
public static boolean match(String pattern, String lookupPath) {
try {
return matcherMethod.invoke(patternsRequestCondition, pattern, lookupPath) != null;
} catch (IllegalAccessException | InvocationTargetException e) {
throw new SaTokenException("路由匹配器调用失败", e);
}
}
/*
性能测试
100万次
new 对象方式耗时3.685s 最慢
反射调方法方式耗时1.311s 中等
原始方式耗时0.445s 最快但有bug
*/
}

View File

@ -60,7 +60,7 @@ public class SaTokenContextForSpring implements SaTokenContext {
*/
@Override
public boolean matchPath(String pattern, String path) {
return SaPathMatcherHolder.getPathMatcher().match(pattern, path);
return SaPatternsRequestConditionHolder.match(pattern, path);
}
/**

View File

@ -60,7 +60,7 @@ public class SaTokenContextForSpringInJakartaServlet implements SaTokenContext {
*/
@Override
public boolean matchPath(String pattern, String path) {
return SaPathMatcherHolder.getPathMatcher().match(pattern, path);
return SaPathPatternParserUtil.match(pattern, path);
}
/**