diff --git a/maku-framework/src/main/java/net/maku/framework/common/xss/XssConfiguration.java b/maku-framework/src/main/java/net/maku/framework/common/xss/XssConfiguration.java index 49719ff..cbc9117 100644 --- a/maku-framework/src/main/java/net/maku/framework/common/xss/XssConfiguration.java +++ b/maku-framework/src/main/java/net/maku/framework/common/xss/XssConfiguration.java @@ -1,10 +1,14 @@ package net.maku.framework.common.xss; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import org.springframework.util.AntPathMatcher; import org.springframework.util.PathMatcher; /** @@ -17,9 +21,10 @@ import org.springframework.util.PathMatcher; @EnableConfigurationProperties(XssProperties.class) @ConditionalOnProperty(prefix = "maku.xss", value = "enabled") public class XssConfiguration { + private final static PathMatcher pathMatcher = new AntPathMatcher(); @Bean - public FilterRegistrationBean xssFilter(XssProperties properties, PathMatcher pathMatcher) { + public FilterRegistrationBean xssFilter(XssProperties properties) { FilterRegistrationBean bean = new FilterRegistrationBean<>(); bean.setFilter(new XssFilter(properties, pathMatcher)); bean.setOrder(Integer.MAX_VALUE); @@ -27,4 +32,19 @@ public class XssConfiguration { return bean; } + + /** + * xss过滤,处理json类型的请求 + */ + @Bean + public ObjectMapper xssFilterObjectMapper(Jackson2ObjectMapperBuilder builder, XssProperties properties) { + ObjectMapper objectMapper = builder.createXmlMapper(false).build(); + + // 注册xss过滤器 + SimpleModule module = new SimpleModule("XssFilterJsonDeserializer"); + module.addDeserializer(String.class, new XssFilterJsonDeserializer(properties, pathMatcher)); + objectMapper.registerModule(module); + + return objectMapper; + } } diff --git a/maku-framework/src/main/java/net/maku/framework/common/xss/XssFilter.java b/maku-framework/src/main/java/net/maku/framework/common/xss/XssFilter.java index 0f2fdeb..0eb0034 100644 --- a/maku-framework/src/main/java/net/maku/framework/common/xss/XssFilter.java +++ b/maku-framework/src/main/java/net/maku/framework/common/xss/XssFilter.java @@ -1,10 +1,12 @@ package net.maku.framework.common.xss; +import cn.hutool.core.util.StrUtil; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.AllArgsConstructor; +import org.springframework.http.MediaType; import org.springframework.util.PathMatcher; import org.springframework.web.filter.OncePerRequestFilter; @@ -29,6 +31,12 @@ public class XssFilter extends OncePerRequestFilter { @Override protected boolean shouldNotFilter(HttpServletRequest request) { + // 如果是json数据,则不处理 + String contentType = request.getContentType(); + if (StrUtil.isBlank(contentType) || StrUtil.startWithIgnoreCase(contentType, MediaType.APPLICATION_JSON_VALUE)) { + return true; + } + // 放行不过滤的URL return properties.getExcludeUrls().stream().anyMatch(excludeUrl -> pathMatcher.match(excludeUrl, request.getRequestURI())); } diff --git a/maku-framework/src/main/java/net/maku/framework/common/xss/XssFilterJsonDeserializer.java b/maku-framework/src/main/java/net/maku/framework/common/xss/XssFilterJsonDeserializer.java new file mode 100644 index 0000000..709e2a0 --- /dev/null +++ b/maku-framework/src/main/java/net/maku/framework/common/xss/XssFilterJsonDeserializer.java @@ -0,0 +1,50 @@ +package net.maku.framework.common.xss; + +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import jakarta.servlet.http.HttpServletRequest; +import lombok.AllArgsConstructor; +import net.maku.framework.common.utils.HttpContextUtils; +import org.springframework.util.PathMatcher; + +import java.io.IOException; + +/** + * xss json过滤 + * + * @author 阿沐 babamu@126.com + * MAKU + */ +@AllArgsConstructor +public class XssFilterJsonDeserializer extends JsonDeserializer { + private final XssProperties properties; + private final PathMatcher pathMatcher; + + @Override + public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + String value = jsonParser.getValueAsString(); + if (StrUtil.isBlank(value)) { + return null; + } + + HttpServletRequest request = HttpContextUtils.getHttpServletRequest(); + if (request == null) { + return value; + } + + // 判断该URI是否放行 + boolean flag = properties.getExcludeUrls().stream().anyMatch(excludeUrl -> pathMatcher.match(excludeUrl, request.getRequestURI())); + if (flag) { + return value; + } + + return XssUtils.filter(value); + } + + @Override + public Class handledType() { + return String.class; + } +} diff --git a/maku-framework/src/main/java/net/maku/framework/common/xss/XssRequestWrapper.java b/maku-framework/src/main/java/net/maku/framework/common/xss/XssRequestWrapper.java index 89deab3..7732744 100644 --- a/maku-framework/src/main/java/net/maku/framework/common/xss/XssRequestWrapper.java +++ b/maku-framework/src/main/java/net/maku/framework/common/xss/XssRequestWrapper.java @@ -1,16 +1,9 @@ package net.maku.framework.common.xss; -import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.StrUtil; -import jakarta.servlet.ReadListener; -import jakarta.servlet.ServletInputStream; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequestWrapper; -import org.springframework.http.MediaType; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.LinkedHashMap; import java.util.Map; @@ -28,41 +21,6 @@ public class XssRequestWrapper extends HttpServletRequestWrapper { } @Override - public ServletInputStream getInputStream() throws IOException { - // 如果是json数据,则不处理 - if (!StrUtil.startWithIgnoreCase(this.getContentType(), MediaType.APPLICATION_JSON_VALUE)) { - return super.getInputStream(); - } - - // 读取内容,进行xss过滤 - String content = IoUtil.readUtf8(super.getInputStream()); - content = filterXss(content); - - // 返回新的 ServletInputStream - final ByteArrayInputStream bis = new ByteArrayInputStream(StrUtil.bytes(content, StandardCharsets.UTF_8)); - return new ServletInputStream() { - @Override - public boolean isFinished() { - return true; - } - - @Override - public boolean isReady() { - return true; - } - - @Override - public void setReadListener(ReadListener readListener) { - } - - @Override - public int read() { - return bis.read(); - } - }; - } - - @Override public String getParameter(String name) { String value = super.getParameter(name);