mirror of https://gitee.com/stylefeng/roses
【validator】新增xss安全模块,修改validator的相关包路径
parent
1178eee90f
commit
698a2bb804
|
@ -19,6 +19,7 @@
|
|||
<module>validator-api</module>
|
||||
<module>validator-sdk-count</module>
|
||||
<module>validator-sdk-black-white</module>
|
||||
<module>validator-sdk-xss</module>
|
||||
<module>validator-business-count</module>
|
||||
<module>validator-spring-boot-starter</module>
|
||||
</modules>
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package cn.stylefeng.roses.kernel.validator.constants;
|
||||
|
||||
/**
|
||||
* XSS模块常量
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/13 23:35
|
||||
*/
|
||||
public interface XssConstants {
|
||||
|
||||
/**
|
||||
* 默认拦截范围
|
||||
*/
|
||||
String DEFAULT_XSS_PATTERN = "/*";
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package cn.stylefeng.roses.kernel.validator.exception;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.stylefeng.roses.kernel.rule.abstracts.AbstractExceptionEnum;
|
||||
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
|
||||
import cn.stylefeng.roses.kernel.validator.constants.ValidatorConstants;
|
||||
|
||||
/**
|
||||
* XSS过滤异常
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/13 23:22
|
||||
*/
|
||||
public class XssFilterException extends ServiceException {
|
||||
|
||||
public XssFilterException(AbstractExceptionEnum exception, Object... params) {
|
||||
super(ValidatorConstants.VALIDATOR_MODULE_NAME, exception.getErrorCode(), StrUtil.format(exception.getUserTip(), params));
|
||||
}
|
||||
|
||||
public XssFilterException(AbstractExceptionEnum exception) {
|
||||
super(ValidatorConstants.VALIDATOR_MODULE_NAME, exception);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package cn.stylefeng.roses.kernel.validator.exception.enums;
|
||||
|
||||
import cn.stylefeng.roses.kernel.rule.abstracts.AbstractExceptionEnum;
|
||||
import cn.stylefeng.roses.kernel.rule.constants.RuleConstants;
|
||||
import cn.stylefeng.roses.kernel.validator.constants.ValidatorConstants;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* XSS过滤异常的枚举
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/13 23:23
|
||||
*/
|
||||
@Getter
|
||||
public enum XssFilterExceptionEnum implements AbstractExceptionEnum {
|
||||
|
||||
/**
|
||||
* XSS初始化配置为空
|
||||
*/
|
||||
CONFIG_IS_NULL(RuleConstants.BUSINESS_ERROR_TYPE_CODE + ValidatorConstants.VALIDATOR_EXCEPTION_STEP_CODE + "11", "XSS初始化配置为空,请检查XSS过滤器配置是否正确!");
|
||||
|
||||
/**
|
||||
* 错误编码
|
||||
*/
|
||||
private final String errorCode;
|
||||
|
||||
/**
|
||||
* 提示用户信息
|
||||
*/
|
||||
private final String userTip;
|
||||
|
||||
XssFilterExceptionEnum(String errorCode, String userTip) {
|
||||
this.errorCode = errorCode;
|
||||
this.userTip = userTip;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package cn.stylefeng.roses.kernel.validator.expander;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.stylefeng.roses.kernel.config.api.context.ConfigContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.stylefeng.roses.kernel.validator.constants.XssConstants.DEFAULT_XSS_PATTERN;
|
||||
|
||||
/**
|
||||
* XSS相关配置
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/13 23:21
|
||||
*/
|
||||
public class XssConfigExpander {
|
||||
|
||||
/**
|
||||
* 获取XSS过滤的url范围
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/13 23:21
|
||||
*/
|
||||
public static String[] getUrlPatterns() {
|
||||
String xssUrlIncludes = ConfigContext.me().getSysConfigValueWithDefault("SYS_XSS_URL_INCLUDES", String.class, DEFAULT_XSS_PATTERN);
|
||||
List<String> split = StrUtil.split(xssUrlIncludes, ',');
|
||||
return ArrayUtil.toArray(split, String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取XSS排除过滤的url范围
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/13 23:21
|
||||
*/
|
||||
public static List<String> getUrlExclusion() {
|
||||
String noneSecurityUrls = ConfigContext.me().getSysConfigValueWithDefault("SYS_XSS_URL_EXCLUSIONS", String.class, "");
|
||||
if (StrUtil.isEmpty(noneSecurityUrls)) {
|
||||
return new ArrayList<>();
|
||||
} else {
|
||||
return StrUtil.split(noneSecurityUrls, ',');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
XSS过滤器,有效防止XSS攻击
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>kernel-d-validator</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>validator-sdk-xss</artifactId>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!--校验模块的api-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>validator-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--web模块-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,51 @@
|
|||
package cn.stylefeng.roses.kemel.xss;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.stylefeng.roses.kemel.xss.prop.XssProperties;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* XSS过滤器,使用 XssHttpServletRequestWrapper 将 HttpServletRequest 对象进行包装
|
||||
* <p>
|
||||
* 用于进行param传参方式的参数过滤掉危险标志
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/13 22:45
|
||||
*/
|
||||
public class XssFilter implements Filter {
|
||||
|
||||
public static final String FILTER_NAME = "GUNS_XSS_FILTER";
|
||||
|
||||
private final XssProperties xssProperties;
|
||||
|
||||
public XssFilter(XssProperties xssProperties) {
|
||||
this.xssProperties = xssProperties;
|
||||
}
|
||||
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
|
||||
String servletPath = httpServletRequest.getServletPath();
|
||||
String contextPath = httpServletRequest.getContextPath();
|
||||
AntPathMatcher antPathMatcher = new AntPathMatcher();
|
||||
|
||||
// 如果当前servlet path排除在外,则放行
|
||||
if (xssProperties != null &&
|
||||
ObjectUtil.isNotEmpty(xssProperties.getUrlExclusion())) {
|
||||
for (String exclusion : xssProperties.getUrlExclusion()) {
|
||||
if (antPathMatcher.match(contextPath + exclusion, servletPath)) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 对原有request对象进行包装
|
||||
chain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) request), response);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package cn.stylefeng.roses.kemel.xss;
|
||||
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
|
||||
/**
|
||||
* 对原有HttpServletRequest包装,在执行获取参数等操作时候,进行xss过滤
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/13 22:50
|
||||
*/
|
||||
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) {
|
||||
super(servletRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有param方式传参的属性的值
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/13 22:52
|
||||
*/
|
||||
public String[] getParameterValues(String parameter) {
|
||||
|
||||
// 获取所有参数
|
||||
String[] values = super.getParameterValues(parameter);
|
||||
if (ObjectUtil.isEmpty(values)) {
|
||||
return values;
|
||||
}
|
||||
|
||||
// 针对每一个string参数进行过滤
|
||||
String[] encodedValues = new String[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
encodedValues[i] = HtmlUtil.filter(values[i]);
|
||||
}
|
||||
|
||||
return encodedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单个param方式传参的属性的值
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/13 22:52
|
||||
*/
|
||||
public String getParameter(String parameter) {
|
||||
String value = super.getParameter(parameter);
|
||||
if (ObjectUtil.isEmpty(value)) {
|
||||
return value;
|
||||
}
|
||||
return HtmlUtil.filter(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取header的值
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/13 22:53
|
||||
*/
|
||||
public String getHeader(String name) {
|
||||
String value = super.getHeader(name);
|
||||
if (ObjectUtil.isEmpty(value)) {
|
||||
return value;
|
||||
}
|
||||
return HtmlUtil.filter(value);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package cn.stylefeng.roses.kemel.xss;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import cn.stylefeng.roses.kemel.xss.prop.XssProperties;
|
||||
import cn.stylefeng.roses.kernel.rule.util.HttpServletUtil;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 针对于jackson反序列化时,xss危险字符串的过滤
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/13 22:56
|
||||
*/
|
||||
public class XssJacksonDeserializer extends JsonDeserializer<String> {
|
||||
|
||||
private final XssProperties xssProperties;
|
||||
|
||||
public XssJacksonDeserializer(XssProperties xssProperties) {
|
||||
this.xssProperties = xssProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
|
||||
String servletPath = HttpServletUtil.getRequest().getServletPath();
|
||||
String contextPath = HttpServletUtil.getRequest().getContextPath();
|
||||
AntPathMatcher antPathMatcher = new AntPathMatcher();
|
||||
|
||||
// 如果当前servlet path排除在外,则放行
|
||||
if (xssProperties != null &&
|
||||
ObjectUtil.isNotEmpty(xssProperties.getUrlExclusion())) {
|
||||
for (String exclusion : xssProperties.getUrlExclusion()) {
|
||||
if (antPathMatcher.match(contextPath + exclusion, servletPath)) {
|
||||
return jsonParser.getText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return HtmlUtil.filter(jsonParser.getText());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package cn.stylefeng.roses.kemel.xss.prop;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Xss的相关配置
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/13 22:46
|
||||
*/
|
||||
@Data
|
||||
public class XssProperties {
|
||||
|
||||
/**
|
||||
* xss过滤的servlet范围,用在设置filter的urlPattern
|
||||
*/
|
||||
private String[] urlPatterns;
|
||||
|
||||
/**
|
||||
* 不被xss过滤的url(ANT风格表达式)
|
||||
*/
|
||||
private List<String> urlExclusion;
|
||||
|
||||
}
|
|
@ -38,6 +38,13 @@
|
|||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!--XSS安全过滤器-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>validator-sdk-xss</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package cn.stylefeng.roses.kernel.validator.starter.resolver;
|
||||
package cn.stylefeng.roses.kernel.validator.starter;
|
||||
|
||||
import cn.stylefeng.roses.kernel.validator.starter.web.GunsValidator;
|
||||
import cn.stylefeng.roses.kernel.validator.starter.web.ValidatorRequestResponseBodyMethodProcessor;
|
||||
import cn.stylefeng.roses.kernel.validator.starter.mvc.GunsValidator;
|
||||
import cn.stylefeng.roses.kernel.validator.starter.mvc.GunsValidatorRequestResponseBodyMethodProcessor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -23,7 +23,7 @@ import java.util.Objects;
|
|||
*/
|
||||
@Configuration
|
||||
@AutoConfigureBefore(ValidationAutoConfiguration.class)
|
||||
public class MethodArgumentResolver {
|
||||
public class MethodArgumentResolverAutoConfiguration {
|
||||
|
||||
@Resource
|
||||
private RequestMappingHandlerAdapter adapter;
|
||||
|
@ -48,7 +48,7 @@ public class MethodArgumentResolver {
|
|||
@PostConstruct
|
||||
public void injectSelfMethodArgumentResolver() {
|
||||
List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();
|
||||
argumentResolvers.add(new ValidatorRequestResponseBodyMethodProcessor(adapter.getMessageConverters()));
|
||||
argumentResolvers.add(new GunsValidatorRequestResponseBodyMethodProcessor(adapter.getMessageConverters()));
|
||||
argumentResolvers.addAll(Objects.requireNonNull(adapter.getArgumentResolvers()));
|
||||
adapter.setArgumentResolvers(argumentResolvers);
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package cn.stylefeng.roses.kernel.validator.starter;
|
||||
|
||||
import cn.stylefeng.roses.kemel.xss.XssFilter;
|
||||
import cn.stylefeng.roses.kemel.xss.XssJacksonDeserializer;
|
||||
import cn.stylefeng.roses.kemel.xss.prop.XssProperties;
|
||||
import cn.stylefeng.roses.kernel.validator.expander.XssConfigExpander;
|
||||
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;
|
||||
|
||||
/**
|
||||
* XSS安全过滤器相关配置
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/13 23:05
|
||||
*/
|
||||
@Configuration
|
||||
public class XssFilterAutoConfiguration {
|
||||
|
||||
/**
|
||||
* XSS Filter过滤器,用来过滤param之类的传参
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/13 23:09
|
||||
*/
|
||||
@Bean
|
||||
public FilterRegistrationBean<XssFilter> xssFilterFilterRegistrationBean() {
|
||||
XssProperties properties = createProperties();
|
||||
|
||||
FilterRegistrationBean<XssFilter> xssFilterFilterRegistrationBean = new FilterRegistrationBean<>();
|
||||
xssFilterFilterRegistrationBean.setFilter(new XssFilter(properties));
|
||||
xssFilterFilterRegistrationBean.addUrlPatterns(properties.getUrlPatterns());
|
||||
xssFilterFilterRegistrationBean.setName(XssFilter.FILTER_NAME);
|
||||
xssFilterFilterRegistrationBean.setOrder(HIGHEST_PRECEDENCE);
|
||||
return xssFilterFilterRegistrationBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* XSS的json反序列化器,针对json的传参
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/13 23:09
|
||||
*/
|
||||
@Bean
|
||||
public Jackson2ObjectMapperBuilderCustomizer xssJackson2ObjectMapperBuilderCustomizer() {
|
||||
return jacksonObjectMapperBuilder ->
|
||||
jacksonObjectMapperBuilder.deserializerByType(String.class, new XssJacksonDeserializer(new XssProperties()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装xss的配置
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/1/13 23:13
|
||||
*/
|
||||
private XssProperties createProperties() {
|
||||
XssProperties xssProperties = new XssProperties();
|
||||
xssProperties.setUrlPatterns(XssConfigExpander.getUrlPatterns());
|
||||
xssProperties.setUrlExclusion(XssConfigExpander.getUrlExclusion());
|
||||
return xssProperties;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package cn.stylefeng.roses.kernel.validator.starter.web;
|
||||
package cn.stylefeng.roses.kernel.validator.starter.mvc;
|
||||
|
||||
import cn.stylefeng.roses.kernel.validator.context.RequestGroupContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
|
@ -1,4 +1,4 @@
|
|||
package cn.stylefeng.roses.kernel.validator.starter.web;
|
||||
package cn.stylefeng.roses.kernel.validator.starter.mvc;
|
||||
|
||||
import cn.stylefeng.roses.kernel.validator.context.RequestParamContext;
|
||||
import org.springframework.core.Conventions;
|
||||
|
@ -21,9 +21,9 @@ import java.util.List;
|
|||
* @author fengshuonan
|
||||
* @date 2020/8/21 20:51
|
||||
*/
|
||||
public class ValidatorRequestResponseBodyMethodProcessor extends RequestResponseBodyMethodProcessor {
|
||||
public class GunsValidatorRequestResponseBodyMethodProcessor extends RequestResponseBodyMethodProcessor {
|
||||
|
||||
public ValidatorRequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters) {
|
||||
public GunsValidatorRequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters) {
|
||||
super(converters);
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* 这个包内容是对spring mvc原有的一些机制的拓展,为了方便校验器对 RequestGroupContext 之类的上下文添值
|
||||
*/
|
||||
package cn.stylefeng.roses.kernel.validator.starter.mvc;
|
|
@ -1,3 +1,4 @@
|
|||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
cn.stylefeng.roses.kernel.validator.starter.ValidatorAutoConfiguration,\
|
||||
cn.stylefeng.roses.kernel.validator.starter.resolver.MethodArgumentResolver
|
||||
cn.stylefeng.roses.kernel.validator.starter.MethodArgumentResolverAutoConfiguration,\
|
||||
cn.stylefeng.roses.kernel.validator.starter.XssFilterAutoConfiguration
|
||||
|
|
Loading…
Reference in New Issue