Spring Gateway中如何高性能地忽略指定URL?
问题
我在Spring Gateway中定义了一个JwtAuthenticationGatewayFilter,目前通过如下方式存储无需令牌校验的忽略URL列表:
public static Predicate<ServerHttpRequest> isApiSecured; @PostConstruct public void initExcludePath(){ isApiSecured = r -> { List<String> excludeList = loginExcludeUrl.getExclude(); return excludeList.stream() .noneMatch(uri -> r.getURI().getPath().equals(uri));}; }
loginExcludeUrl.getExclude()会获取包含成百上千条数据的忽略URL列表。在过滤器中通过以下代码判断是否需要校验:
if (isApiSecured.test(request)) { return authImpl(request, exchange, chain); }
我担心当忽略列表增大时,每个请求都要执行列表过滤会导致网站整体性能下降。请问有没有更优的URL忽略方案?
部分URL配置如下:
login-check-path: exclude: - /post/user/reg - /login - /post/user/login - /post/user/plugin/login - /post/user/sms - /post/auth/access_token/refresh - /post/auth/refresh_token/refresh - /post/article/newstories - /post/article/originalstories - /post/article/officialstories - /post/article/share - /post/article/read - /post/article/detail - /post/wechat/util/verifyWxToken - /post/wechat/login/getQRCodeUrl - /post/wechat/login/wxCallBack - /post/alipay/login/alipayCallBack - /post/alipay/login/getQRCodeUrl - /post/alipay/notification/v1/alipaySeverNotification - /post/websocket - /manage/admin/user/login - /dict/user/plugin/login - /dict/user/login - /dict/auth/access_token/refresh - /dict/auth/refresh_token/refresh - /fortune/user/login - /fortune/user/guest/login - /fortune/user/sms - /fortune/user/reg/verify - /fortune/auth/access-token/refresh - /tik/user/login - /tik/user/guest/login - /tik/user/sms - /tik/user/reg/verify
优化方案
1. 改用HashSet存储忽略URL,提升匹配效率
List的noneMatch是线性遍历,时间复杂度O(n);而HashSet的contains是哈希查找,时间复杂度O(1),数据量越大性能提升越明显。
修改代码如下:
private Set<String> excludeUrlSet; @PostConstruct public void initExcludePath(){ excludeUrlSet = new HashSet<>(loginExcludeUrl.getExclude()); isApiSecured = r -> !excludeUrlSet.contains(r.getURI().getPath()); }
2. 使用Spring Gateway自带路径匹配工具支持通配符
如果忽略URL有规律(比如同一前缀、后缀),可以用PathPatternMatcher支持通配符(如/post/**、/**/login),减少配置条目,同时匹配效率更高。
示例:
- 简化yaml配置,用通配符合并相似路径:
login-check-path: exclude: - /login - /post/user/reg - /post/user/login* - /post/user/sms - /post/auth/** - /post/article/** - /post/wechat/** - /post/alipay/** - /post/websocket - /manage/admin/user/login - /dict/user/login* - /dict/auth/** - /fortune/user/login* - /fortune/user/sms - /fortune/user/reg/verify - /fortune/auth/** - /tik/user/login* - /tik/user/sms - /tik/user/reg/verify
- 修改过滤器初始化代码:
private List<PathPattern> excludePatterns; private final PathPatternMatcher pathMatcher = new PathPatternMatcher(); @PostConstruct public void initExcludePath(){ excludePatterns = loginExcludeUrl.getExclude().stream() .map(pathMatcher::parse) .collect(Collectors.toList()); isApiSecured = r -> excludePatterns.stream() .noneMatch(pattern -> pathMatcher.match(pattern, r.getURI().getPath())); }
3. 路由层面跳过过滤器(性能最优)
在Gateway路由配置中,给无需校验的路径单独配置路由,不绑定JwtAuthenticationGatewayFilter,这样请求根本不会进入过滤器,完全避免匹配开销。
示例路由配置:
spring: cloud: gateway: routes: # 无需校验的路由 - id: exclude_routes uri: lb://your-service predicates: - Path=/login,/post/user/reg,/post/user/login**,/post/auth/**,/post/article/** filters: - StripPrefix=0 # 需要校验的路由 - id: secured_routes uri: lb://your-service predicates: - Path=/** filters: - JwtAuthenticationGatewayFilter
4. 缓存请求路径,减少重复解析
每次调用r.getURI().getPath()都会重新解析路径,可提前缓存路径值,减少重复计算:
isApiSecured = r -> { String path = r.getURI().getPath(); return !excludeUrlSet.contains(path); };
内容的提问来源于stack exchange,提问作者Dolphin
相关产品推荐
相关产品推荐

