This commit is contained in:
itcast 2025-10-23 18:25:57 +08:00
parent acad3beb85
commit 9f613a2d22
1 changed files with 116 additions and 0 deletions

View File

@ -0,0 +1,116 @@
package com.bonus.sgzb.gateway.filter;
/**
* @author 30791
* @version 1.0
* Create by 2025/10/22 10:41
*/
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
@Slf4j
@Component
public class SecurityHeaderFilter implements GlobalFilter, Ordered {
// 定义需要添加安全头的页面路径
private static final List<String> PROTECTED_PATH_PREFIXES = Arrays.asList(
"/login", "/admin", "/PMA", "/html", "/sitemap.xml",
"/db", "/dbadmin", "/myadmin", "/mysql", "/mysqladmin",
"/phpMyAdmin", "/phpMyAdmin2"
);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
String path = request.getPath().value();
String method = request.getMethod().name();
log.debug("SecurityHeaderFilter 处理请求: {} {}", method, path);
// 获取响应头
HttpHeaders headers = response.getHeaders();
// 检查是否为受保护的路径
boolean isProtectedPage = isProtectedPath(path);
// 设置 Referrer-Policy所有请求都应用
headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
if (isProtectedPage) {
// 添加点击劫持防护头
headers.add("X-Frame-Options", "DENY");
// 改成完整的 CSP仍保留禁止被嵌套
headers.add("Content-Security-Policy",
"default-src 'self'; " + // 默认仅本域
"script-src 'self' https://code.jquery.com; " + // 允许 jQuery CDN
"style-src 'self' 'unsafe-inline'; " + // 本域 CSS + 内联
"img-src 'self' data:; " + // 本域图片data URI
"frame-ancestors 'none'"); // 继续禁止被 iframe
// 添加其他推荐的安全头
// headers.add("X-Content-Type-Options", "nosniff");
// headers.add("X-XSS-Protection", "1; mode=block");
// headers.add("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
log.info("为受保护路径 {} 添加严格安全头部", path);
// 调试日志
if (log.isDebugEnabled()) {
log.debug("设置的严格安全头部:");
log.debug(" X-Frame-Options: DENY");
log.debug(" Content-Security-Policy: frame-ancestors 'none'");
}
} else {
// 即使不是受保护路径也添加基本安全头
headers.add("X-Frame-Options", "SAMEORIGIN");
// 改成完整的 CSP仍保留禁止被嵌套
headers.add("Content-Security-Policy",
"default-src 'self'; " + // 默认仅本域
"script-src 'self' https://code.jquery.com; " + // 允许 jQuery CDN
"style-src 'self' 'unsafe-inline'; " + // 本域 CSS + 内联
"img-src 'self' data:; " + // 本域图片data URI
"frame-ancestors 'none'"); // 继续禁止被 iframe
//headers.add("X-Content-Type-Options", "nosniff");
//headers.add("X-XSS-Protection", "1; mode=block");
log.debug("为普通路径 {} 添加基本安全头部", path);
}
// 继续执行过滤器链
return chain.filter(exchange);
}
@Override
public int getOrder() {
// 设置过滤器执行顺序数字越小优先级越高
// 设置为高位值确保在路由之后执行能够修改响应头
return Ordered.LOWEST_PRECEDENCE;
}
/**
* 检查请求路径是否为受保护路径
*/
private boolean isProtectedPath(String requestPath) {
for (String prefix : PROTECTED_PATH_PREFIXES) {
if (requestPath.startsWith(prefix)) {
log.debug("路径 {} 匹配受保护前缀: {}", requestPath, prefix);
return true;
}
}
return false;
}
}