|
|
|
@ -8,7 +8,7 @@ import jakarta.servlet.FilterConfig; |
|
|
|
import jakarta.servlet.ServletException; |
|
|
|
import jakarta.servlet.ServletRequest; |
|
|
|
import jakarta.servlet.ServletResponse; |
|
|
|
import javax.servlet.annotation.WebFilter; |
|
|
|
import jakarta.servlet.annotation.WebFilter; |
|
|
|
import jakarta.servlet.http.HttpServletRequest; |
|
|
|
import jakarta.servlet.http.HttpServletResponse; |
|
|
|
|
|
|
|
@ -17,16 +17,20 @@ import org.slf4j.LoggerFactory; |
|
|
|
import org.springframework.beans.factory.annotation.Value; |
|
|
|
import java.util.UUID; |
|
|
|
|
|
|
|
import org.apache.commons.lang3.StringUtils; |
|
|
|
import org.jboss.logging.MDC; |
|
|
|
|
|
|
|
@WebFilter |
|
|
|
public class CrosXssFilter implements Filter { |
|
|
|
|
|
|
|
|
|
|
|
private static final Logger logger = LoggerFactory.getLogger(CrosXssFilter.class); |
|
|
|
|
|
|
|
|
|
|
|
@Value("${crosxss.filter.disable:false}") |
|
|
|
private boolean disable; |
|
|
|
|
|
|
|
@Value("${response.access.control.allow.origin:*}") |
|
|
|
private String accessControlAllowOrigin; |
|
|
|
|
|
|
|
@Override |
|
|
|
public void init(FilterConfig filterConfig) throws ServletException { |
|
|
|
} |
|
|
|
@ -37,34 +41,89 @@ public class CrosXssFilter implements Filter { |
|
|
|
try { |
|
|
|
MDC.put("processNo", UUID.randomUUID().toString().replace("-", "")); |
|
|
|
request.setCharacterEncoding("utf-8"); |
|
|
|
// response.setContentType("text/html;charset=utf-8");
|
|
|
|
response.setContentType("application/json;charset=UTF-8"); |
|
|
|
if (disable) { |
|
|
|
chain.doFilter(request, response); |
|
|
|
} else { |
|
|
|
//跨域设置
|
|
|
|
if (response instanceof HttpServletResponse) { |
|
|
|
|
|
|
|
HttpServletResponse httpServletResponse = (HttpServletResponse) response; |
|
|
|
//禁用浏览器缓存
|
|
|
|
httpServletResponse.setHeader("Cache-Control", "no-store"); |
|
|
|
//禁止被IFrame嵌套
|
|
|
|
httpServletResponse.setHeader("X-Frame-Options", "deny"); |
|
|
|
//安全性配置
|
|
|
|
HttpServletRequest httpRequest = (HttpServletRequest) request; |
|
|
|
|
|
|
|
String referer = httpRequest.getHeader("Referer"); |
|
|
|
if (StringUtils.isNotBlank(referer) && !"*".equals(accessControlAllowOrigin)) { |
|
|
|
// 允许多个域名,逗号分隔
|
|
|
|
String[] allowedOrigins = accessControlAllowOrigin.split(","); |
|
|
|
boolean matched = false; |
|
|
|
for (String origin : allowedOrigins) { |
|
|
|
origin = origin.trim(); |
|
|
|
if (StringUtils.isNotBlank(origin) && referer.startsWith(origin)) { |
|
|
|
matched = true; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
// 如果一个都不匹配,则返回 403
|
|
|
|
if (!matched) { |
|
|
|
httpServletResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Invalid Referer"); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
httpServletResponse.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0"); |
|
|
|
httpServletResponse.setHeader("Pragma", "no-cache"); |
|
|
|
httpServletResponse.setDateHeader("Expires", 0); |
|
|
|
|
|
|
|
httpServletResponse.setHeader("X-Frame-Options", "SAMEORIGIN"); |
|
|
|
|
|
|
|
String nonce = UUID.randomUUID().toString().replace("-", "").substring(0, 16); // 生成随机 nonce
|
|
|
|
httpServletResponse.setHeader("Content-Security-Policy", |
|
|
|
"default-src 'self'; " + |
|
|
|
"img-src 'self' data:; "+ |
|
|
|
"font-src 'self' https://i.alicdn.com data:; "+ //阿里系的ui组件
|
|
|
|
// "script-src 'self' 'nonce-" + nonce + "'; " + //nonce针对内联 JavaScript
|
|
|
|
// "style-src 'self' 'nonce-" + nonce + "'; " + //nonce针对内联 CSS
|
|
|
|
"script-src 'self'; " + |
|
|
|
"style-src 'self'; " + |
|
|
|
"object-src 'none'; " + |
|
|
|
"base-uri 'none'; " + |
|
|
|
"form-action 'self'; " + |
|
|
|
"frame-ancestors 'none'" |
|
|
|
); |
|
|
|
httpServletResponse.setHeader("X-XSS-Protection", "1; mode=block"); |
|
|
|
httpServletResponse.setHeader("X-Content-Type-Options", "nosniff"); |
|
|
|
httpServletResponse.setHeader("Referrer-Policy", "origin"); |
|
|
|
httpServletResponse.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload"); |
|
|
|
|
|
|
|
//add
|
|
|
|
httpServletResponse.addHeader("Vary", "Origin"); |
|
|
|
httpServletResponse.addHeader("Vary", "Access-Control-Request-Method"); |
|
|
|
httpServletResponse.addHeader("Vary", "Access-Control-Request-Headers"); |
|
|
|
|
|
|
|
httpServletResponse.setHeader("Access-Control-Allow-Headers", "*"); |
|
|
|
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS"); |
|
|
|
// 设置允许的域名
|
|
|
|
httpServletResponse.setHeader("Access-Control-Allow-Origin", accessControlAllowOrigin); |
|
|
|
httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ("OPTIONS".equals(((HttpServletRequest) request).getMethod())) { |
|
|
|
httpServletResponse.setStatus(HttpServletResponse.SC_OK); // 200
|
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
ServletRequest requestWrapper = null; |
|
|
|
if(request instanceof HttpServletRequest) { |
|
|
|
requestWrapper = new RequestWrapper((HttpServletRequest) request); |
|
|
|
} |
|
|
|
if(requestWrapper == null) { |
|
|
|
chain.doFilter(request, response); |
|
|
|
chain.doFilter(request, response); |
|
|
|
} else { |
|
|
|
chain.doFilter(requestWrapper, response); |
|
|
|
chain.doFilter(requestWrapper, response); |
|
|
|
} |
|
|
|
} |
|
|
|
} finally { |
|
|
|
// 避免线程泄漏
|
|
|
|
MDC.clear(); |
|
|
|
} |
|
|
|
|
|
|
|
|