springboot 实现xss过滤 Published on Sep 15, 2023 in 随笔 with 0 comment ```java package com.jinw.cms.aspectj; import com.jinw.cms.aspectj.annotation.XssIgnore; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.AsyncHandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Objects; public class XssInterceptor implements AsyncHandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; XssIgnore xssIgnore = handlerMethod.getMethodAnnotation(XssIgnore.class); if (Objects.nonNull(xssIgnore)) { XssContextHolder.ignore(); } } return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { XssContextHolder.remove(); } /** * 如果返回一个current类型的变量,会启用一个新的线程。执行完preHandle方法之后立即会调用afterConcurrentHandlingStarted * 然后新线程再以次执行preHandle,postHandle,afterCompletion** */ @Override public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { XssContextHolder.remove(); } } ``` ```java package com.jinw.cms.aspectj; import com.fasterxml.jackson.core.JacksonException; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.type.LogicalType; import com.jinw.cms.entity.XssMode; import com.jinw.utils.cms.HtmlUtils; import com.jinw.utils.cms.StringUtils; import lombok.RequiredArgsConstructor; import java.io.IOException; import java.util.Objects; @RequiredArgsConstructor public class XssDeserializer extends JsonDeserializer { private final XssMode mode; @Override public LogicalType logicalType() { return LogicalType.Textual; } @Override public String getNullValue(DeserializationContext ctxt) throws JsonMappingException { return StringUtils.EMPTY; } @Override public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { if (p.hasToken(JsonToken.VALUE_STRING)) { return deal(p.getText()); } JsonToken token = p.getCurrentToken(); if (token.isScalarValue()) { String text = p.getValueAsString(); if (text != null) { return text; } } return (String) ctxt.handleUnexpectedToken(String.class, p); } private String deal(String text) { if (Objects.nonNull(text) && !XssContextHolder.isIgnore()) { if (mode == XssMode.CLEAN) { text = HtmlUtils.clean(text); } else { text = HtmlUtils.escape(text); } } return text; } } ``` ```java package com.jinw.cms.aspectj; import java.util.Objects; public class XssContextHolder { private static final ThreadLocal CONTEXT = new ThreadLocal<>(); /** * 默认为true */ public static boolean isIgnore() { return Objects.isNull(CONTEXT.get()) ? true : CONTEXT.get(); } public static void ignore() { CONTEXT.set(true); } public static void remove() { CONTEXT.remove(); } } ``` ```java package com.jinw.cms.config.properties; import com.jinw.cms.entity.XssMode; import lombok.Getter; import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import java.util.List; @Setter @Getter @ConfigurationProperties(prefix = "xss") public class XssProperties { /** * 是否开启XSS过滤 */ private boolean enabled; /** * 处理方式 */ private XssMode mode; /** * 不进行处理的路径 */ private List excludes; /** * 处理指定路径 */ private List urlPatterns; } ``` ```java package com.jinw.cms.config; import com.google.common.collect.Lists; import com.jinw.cms.aspectj.XssDeserializer; import com.jinw.cms.aspectj.XssInterceptor; import com.jinw.cms.config.properties.XssProperties; import com.jinw.utils.cms.StringUtils; import lombok.RequiredArgsConstructor; import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.Collections; import java.util.List; @RequiredArgsConstructor @Configuration @EnableConfigurationProperties(XssProperties.class) public class XssConfig implements WebMvcConfigurer { private final XssProperties xssProperties; @Override public void addInterceptors(InterceptorRegistry registry) { if (xssProperties.isEnabled()) { List urlPatterns = xssProperties.getUrlPatterns(); if (StringUtils.isEmpty(urlPatterns)) { urlPatterns = Lists.newArrayList(); urlPatterns.add("/**"); } List excludes = xssProperties.getExcludes(); if (StringUtils.isEmpty(excludes)) { excludes = Collections.emptyList(); } //addPathPatterns(urlPatterns) 对所有请求都拦截,但是排除了 excludePathPatterns(excludes) 请求的拦截。 registry.addInterceptor(new XssInterceptor()). addPathPatterns(urlPatterns) .excludePathPatterns(excludes).order(Ordered.LOWEST_PRECEDENCE); } } @Bean public Jackson2ObjectMapperBuilderCustomizer xssCustomizer() { return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.deserializerByType(String.class, new XssDeserializer(xssProperties.getMode())); } } ``` ```java package com.jinw.cms.aspectj.annotation; import java.lang.annotation.*; @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface XssIgnore { } ``` ```yaml # 防止XSS攻击 xss: # 过滤开关 enabled: true mode: clean ``` 本文由 admin 创作,采用 知识共享署名4.0 国际许可协议进行许可。本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。