package com.vci.ubcs.gateway.filter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import javax.annotation.PostConstruct; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.regex.Pattern; /** * 全局URL安全过滤器 - 对所有请求生效 * 直接使用@Value注解读取配置 */ @Component public class GlobalUrlSecurityFilter implements GlobalFilter, Ordered { private static final Logger log = LoggerFactory.getLogger(GlobalUrlSecurityFilter.class); // 是否启用URL安全过滤器 @Value("${gateway.security.url.enabled:true}") private boolean enabled; // 危险字符正则表达式 @Value("${gateway.security.url.dangerous-pattern:\\x00-\\x1F\\x7F]|%3C|%3E|%27|%22|%00|\\.\\./|\\.\\.\\\\}") private String dangerousPattern; // 白名单路径 @Value("${gateway.security.url.whitelist-paths:/health,/actuator/health,/actuator/info,/favicon.ico}") private String[] whitelistPaths; private Pattern compiledPattern; private Set whitelistSet; /** * 初始化方法 */ @PostConstruct public void init() { try { // 编译危险字符正则表达式 this.compiledPattern = Pattern.compile(dangerousPattern, Pattern.CASE_INSENSITIVE); // 初始化白名单集合 this.whitelistSet = new HashSet<>(Arrays.asList(whitelistPaths)); log.info("全局URL安全过滤器初始化完成"); log.info("过滤器状态: {}", enabled ? "已启用" : "已禁用"); log.info("白名单路径: {}", whitelistSet); } catch (Exception e) { log.error("初始化过滤器失败", e); // 使用默认配置作为后备 this.compiledPattern = Pattern.compile( "\\x00-\\x1F\\x7F]|%3C|%3E|%27|%22|%00|\\.\\./|\\.\\.\\\\", Pattern.CASE_INSENSITIVE ); this.whitelistSet = new HashSet<>(Arrays.asList( "/health", "/actuator/health", "/actuator/info", "/favicon.ico" )); } } @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 检查是否启用过滤器 if (!enabled) { return chain.filter(exchange); } ServerHttpRequest request = exchange.getRequest(); String path = request.getURI().getPath(); String method = request.getMethod().name(); // 检查白名单路径 if (isWhitelistedPath(path)) { return chain.filter(exchange); } // 验证路径安全性 if (!isPathSafe(path)) { log.warn("拦截危险请求: {} {}", method, request.getURI()); exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } /** * 验证路径安全性 */ private boolean isPathSafe(String path) { if (path == null || path.isEmpty()) { return true; } // 检查路径遍历 if (path.contains("../") || path.contains("..\\")) { return false; } // 检查危险字符 if (compiledPattern.matcher(path).find()) { return false; } return true; } /** * 检查路径是否在白名单中 */ private boolean isWhitelistedPath(String path) { if (path == null) { return false; } for (String whitelistPath : whitelistSet) { if (path.startsWith(whitelistPath) || path.equals(whitelistPath)) { return true; } } return false; } }