refactor(web): 优化 XSS 过滤部分代码

This commit is contained in:
2024-03-30 21:42:51 +08:00
parent 2656da450c
commit 8c91d4a26c
6 changed files with 76 additions and 80 deletions

View File

@@ -37,7 +37,7 @@ public class PropertiesConstants {
/** /**
* 线程池配置 * 线程池配置
*/ */
public static final String THREAD_POOL = CONTINEW_STARTER + ".thread-pool"; public static final String THREAD_POOL = CONTINEW_STARTER + StringConstants.DOT + "thread-pool";
/** /**
* Spring Doc 配置 * Spring Doc 配置
@@ -47,7 +47,7 @@ public class PropertiesConstants {
/** /**
* Spring Doc Swagger UI 配置 * Spring Doc Swagger UI 配置
*/ */
public static final String SPRINGDOC_SWAGGER_UI = SPRINGDOC + ".swagger-ui"; public static final String SPRINGDOC_SWAGGER_UI = SPRINGDOC + StringConstants.DOT + "swagger-ui";
/** /**
* 安全配置 * 安全配置
@@ -67,52 +67,52 @@ public class PropertiesConstants {
/** /**
* Web 配置 * Web 配置
*/ */
public static final String WEB = CONTINEW_STARTER + ".web"; public static final String WEB = CONTINEW_STARTER + StringConstants.DOT + "web";
/** /**
* 跨域配置 * 跨域配置
*/ */
public static final String CORS = WEB + ".cors"; public static final String CORS = WEB + StringConstants.DOT + "cors";
/** /**
* 链路配置 * 链路配置
*/ */
public static final String TRACE = WEB + ".trace"; public static final String TRACE = WEB + StringConstants.DOT + "trace";
/** /**
* 链路配置 * XSS 配置
*/ */
public static final String XSS = WEB + ".xss"; public static final String XSS = WEB + StringConstants.DOT + "xss";
/** /**
* 日志配置 * 日志配置
*/ */
public static final String LOG = CONTINEW_STARTER + ".log"; public static final String LOG = CONTINEW_STARTER + StringConstants.DOT + "log";
/** /**
* 存储配置 * 存储配置
*/ */
public static final String STORAGE = CONTINEW_STARTER + ".storage"; public static final String STORAGE = CONTINEW_STARTER + StringConstants.DOT + "storage";
/** /**
* 本地存储配置 * 本地存储配置
*/ */
public static final String STORAGE_LOCAL = STORAGE + ".local"; public static final String STORAGE_LOCAL = STORAGE + StringConstants.DOT + "local";
/** /**
* 验证码配置 * 验证码配置
*/ */
public static final String CAPTCHA = CONTINEW_STARTER + ".captcha"; public static final String CAPTCHA = CONTINEW_STARTER + StringConstants.DOT + "captcha";
/** /**
* 图形验证码配置 * 图形验证码配置
*/ */
public static final String CAPTCHA_GRAPHIC = CAPTCHA + ".graphic"; public static final String CAPTCHA_GRAPHIC = CAPTCHA + StringConstants.DOT + "graphic";
/** /**
* 行为验证码配置 * 行为验证码配置
*/ */
public static final String CAPTCHA_BEHAVIOR = CAPTCHA + ".behavior"; public static final String CAPTCHA_BEHAVIOR = CAPTCHA + StringConstants.DOT + "behavior";
private PropertiesConstants() { private PropertiesConstants() {
} }

View File

@@ -25,13 +25,13 @@ import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import top.charles7c.continew.starter.core.constant.PropertiesConstants; import top.charles7c.continew.starter.core.constant.PropertiesConstants;
import top.charles7c.continew.starter.core.constant.StringConstants;
/** /**
* 链路跟踪自动配置 * 链路跟踪自动配置
@@ -41,6 +41,7 @@ import top.charles7c.continew.starter.core.constant.StringConstants;
* @since 1.3.0 * @since 1.3.0
*/ */
@AutoConfiguration @AutoConfiguration
@ConditionalOnWebApplication
@EnableConfigurationProperties(TraceProperties.class) @EnableConfigurationProperties(TraceProperties.class)
@ConditionalOnProperty(prefix = PropertiesConstants.TRACE, name = PropertiesConstants.ENABLED, havingValue = "true") @ConditionalOnProperty(prefix = PropertiesConstants.TRACE, name = PropertiesConstants.ENABLED, havingValue = "true")
public class TraceAutoConfiguration { public class TraceAutoConfiguration {
@@ -70,10 +71,9 @@ public class TraceAutoConfiguration {
* TLog 过滤器配置 * TLog 过滤器配置
*/ */
@Bean @Bean
public FilterRegistrationBean<TLogServletFilter> filterRegistration() { public FilterRegistrationBean<TLogServletFilter> tLogServletFilter() {
FilterRegistrationBean<TLogServletFilter> registration = new FilterRegistrationBean<>(); FilterRegistrationBean<TLogServletFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new TLogServletFilter(traceProperties)); registration.setFilter(new TLogServletFilter(traceProperties));
registration.addUrlPatterns(StringConstants.PATH_PATTERN_CURRENT_DIR);
registration.setOrder(Ordered.HIGHEST_PRECEDENCE); registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration; return registration;
} }

View File

@@ -25,18 +25,22 @@ import org.springframework.context.annotation.Bean;
import top.charles7c.continew.starter.core.constant.PropertiesConstants; import top.charles7c.continew.starter.core.constant.PropertiesConstants;
/** /**
* XSS配置 * XSS 过滤自动配置
* *
* @author whhya * @author whhya
* @since 1.0.0 * @since 2.0.0
*/ */
@AutoConfiguration @AutoConfiguration
@ConditionalOnWebApplication @ConditionalOnWebApplication
@ConditionalOnProperty(prefix = PropertiesConstants.XSS, name = PropertiesConstants.ENABLED, havingValue = "true")
@EnableConfigurationProperties(XssProperties.class) @EnableConfigurationProperties(XssProperties.class)
@ConditionalOnProperty(prefix = PropertiesConstants.XSS, name = PropertiesConstants.ENABLED, havingValue = "true")
public class XssAutoConfiguration { public class XssAutoConfiguration {
/**
* XSS 过滤器配置
*/
@Bean @Bean
public FilterRegistrationBean<XssFilter> XssFilter(XssProperties xssProperties) { public FilterRegistrationBean<XssFilter> xssFilter(XssProperties xssProperties) {
FilterRegistrationBean<XssFilter> registrationBean = new FilterRegistrationBean<>(); FilterRegistrationBean<XssFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new XssFilter(xssProperties)); registrationBean.setFilter(new XssFilter(xssProperties));
return registrationBean; return registrationBean;

View File

@@ -29,10 +29,10 @@ import java.io.IOException;
import java.util.List; import java.util.List;
/** /**
* xss过滤器 * XSS 过滤器
* *
* @author whhya * @author whhya
* @since 1.0.0 * @since 2.0.0
*/ */
public class XssFilter implements Filter { public class XssFilter implements Filter {
@@ -53,58 +53,46 @@ public class XssFilter implements Filter {
public void doFilter(ServletRequest servletRequest, public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException { FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest; // 未开启 XSS 过滤,则直接跳过
//未开启xss过滤则直接跳过 if (servletRequest instanceof HttpServletRequest request && xssProperties.isEnabled()) {
if (!xssProperties.isEnabled()) { // 放行路由:忽略 XSS 过滤()
filterChain.doFilter(req, servletResponse); List<String> excludePatterns = xssProperties.getExcludePatterns();
if (CollectionUtil.isNotEmpty(excludePatterns) && isMatchPath(request.getServletPath(), excludePatterns)) {
filterChain.doFilter(request, servletResponse);
return; return;
} }
// 拦截路由:执行 XSS 过滤
//限定url地址 List<String> includePatterns = xssProperties.getIncludePatterns();
List<String> pathPatterns = xssProperties.getPathPatterns(); if (CollectionUtil.isNotEmpty(includePatterns)) {
if (isMatchPath(request.getServletPath(), includePatterns)) {
//判断是否匹配需要忽略地址 filterChain.doFilter(new XssServletRequestWrapper(request), servletResponse);
List<String> pathExcludePatterns = xssProperties.getPathExcludePatterns();
if (CollectionUtil.isNotEmpty(pathPatterns)) {
if (isMatchPath(req.getServletPath(), pathExcludePatterns)) {
filterChain.doFilter(req, servletResponse);
return;
}
}
//如果存在则限定path拦截
if (CollectionUtil.isNotEmpty(pathPatterns)) {
//未匹配上限定地址,则直接不过滤
if (isMatchPath(req.getServletPath(), pathPatterns)) {
filterChain.doFilter(new XssServletRequestWrapper(req), servletResponse);
return;
} else { } else {
filterChain.doFilter(req, servletResponse); filterChain.doFilter(request, servletResponse);
}
return; return;
} }
// 默认:执行 XSS 过滤
filterChain.doFilter(new XssServletRequestWrapper(request), servletResponse);
return;
} }
filterChain.doFilter(servletRequest, servletResponse);
//默认拦截
filterChain.doFilter(new XssServletRequestWrapper((HttpServletRequest) servletRequest), servletResponse);
} }
/** /**
* 判断数组中是否存在匹配的路径 * 判断数组中是否存在匹配的路径
* *
* @param requestURL 请求地址 * @param requestUrl 请求地址
* @param pathPatterns 指定匹配路径 * @param pathPatterns 指定匹配路径
* @return true 匹配 false 不匹配 * @return true匹配false不匹配
*/ */
private static boolean isMatchPath(String requestURL, List<String> pathPatterns) { private static boolean isMatchPath(String requestUrl, List<String> pathPatterns) {
for (String pattern : pathPatterns) { for (String pattern : pathPatterns) {
PathPattern pathPattern = PathPatternParser.defaultInstance.parse(pattern); PathPattern pathPattern = PathPatternParser.defaultInstance.parse(pattern);
PathContainer pathContainer = PathContainer.parsePath(requestURL); PathContainer pathContainer = PathContainer.parsePath(requestUrl);
boolean matches = pathPattern.matches(pathContainer); if (pathPattern.matches(pathContainer)) {
if (matches) {
return true; return true;
} }
} }
return false; return false;
} }
} }

View File

@@ -23,27 +23,32 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* xss配置属性 * XSS 过滤配置属性
* *
* @author whhya * @author whhya
* @since 1.0.0 * @since 2.0.0
*/ */
@ConfigurationProperties(PropertiesConstants.XSS) @ConfigurationProperties(PropertiesConstants.XSS)
public class XssProperties { public class XssProperties {
/** /**
* 是否启用Xss * 是否启用 XSS 过滤
*/ */
private boolean enabled = true; private boolean enabled = true;
/** /**
* 拦截路由默认为空 * 拦截路由默认为空
*
* <p>
* 当拦截的路由配置不为空,则根据该配置执行过滤
* </p>
*/ */
private List<String> pathPatterns = new ArrayList<>(); private List<String> includePatterns = new ArrayList<>();
/** /**
* 放行路由默认为空 * 放行路由默认为空
*/ */
private List<String> pathExcludePatterns = new ArrayList<>(); private List<String> excludePatterns = new ArrayList<>();
public boolean isEnabled() { public boolean isEnabled() {
return enabled; return enabled;
@@ -53,20 +58,19 @@ public class XssProperties {
this.enabled = enabled; this.enabled = enabled;
} }
public List<String> getPathPatterns() { public List<String> getIncludePatterns() {
return pathPatterns; return includePatterns;
} }
public void setPathPatterns(List<String> pathPatterns) { public void setIncludePatterns(List<String> includePatterns) {
this.pathPatterns = pathPatterns; this.includePatterns = includePatterns;
} }
public List<String> getPathExcludePatterns() { public List<String> getExcludePatterns() {
return pathExcludePatterns; return excludePatterns;
} }
public void setPathExcludePatterns(List<String> pathExcludePatterns) { public void setExcludePatterns(List<String> excludePatterns) {
this.pathExcludePatterns = pathExcludePatterns; this.excludePatterns = excludePatterns;
} }
} }

View File

@@ -31,10 +31,10 @@ import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
/** /**
* 针对XssServletRequest进行过滤的包装类 * 针对 XssServletRequest 进行过滤的包装类
* *
* @author whh * @author whh
* @since 1.0.0 * @since 2.0.0
*/ */
public class XssServletRequestWrapper extends HttpServletRequestWrapper { public class XssServletRequestWrapper extends HttpServletRequestWrapper {
@@ -102,6 +102,7 @@ public class XssServletRequestWrapper extends HttpServletRequestWrapper {
static ServletInputStream getServletInputStream(String body) { static ServletInputStream getServletInputStream(String body) {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes()); final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
return new ServletInputStream() { return new ServletInputStream() {
@Override
public int read() { public int read() {
return byteArrayInputStream.read(); return byteArrayInputStream.read();
} }
@@ -118,9 +119,8 @@ public class XssServletRequestWrapper extends HttpServletRequestWrapper {
@Override @Override
public void setReadListener(ReadListener readListener) { public void setReadListener(ReadListener readListener) {
// 设置监听器
} }
}; };
} }
} }