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|\\.\\./|\\.\\.\\\\",
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;
}
}