重构xss
This commit is contained in:
parent
60cccc3932
commit
92b78f57ba
|
@ -1,10 +1,14 @@
|
||||||
package net.maku.framework.common.xss;
|
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.autoconfigure.condition.ConditionalOnProperty;
|
||||||
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.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||||
|
import org.springframework.util.AntPathMatcher;
|
||||||
import org.springframework.util.PathMatcher;
|
import org.springframework.util.PathMatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,9 +21,10 @@ import org.springframework.util.PathMatcher;
|
||||||
@EnableConfigurationProperties(XssProperties.class)
|
@EnableConfigurationProperties(XssProperties.class)
|
||||||
@ConditionalOnProperty(prefix = "maku.xss", value = "enabled")
|
@ConditionalOnProperty(prefix = "maku.xss", value = "enabled")
|
||||||
public class XssConfiguration {
|
public class XssConfiguration {
|
||||||
|
private final static PathMatcher pathMatcher = new AntPathMatcher();
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public FilterRegistrationBean<XssFilter> xssFilter(XssProperties properties, PathMatcher pathMatcher) {
|
public FilterRegistrationBean<XssFilter> xssFilter(XssProperties properties) {
|
||||||
FilterRegistrationBean<XssFilter> bean = new FilterRegistrationBean<>();
|
FilterRegistrationBean<XssFilter> bean = new FilterRegistrationBean<>();
|
||||||
bean.setFilter(new XssFilter(properties, pathMatcher));
|
bean.setFilter(new XssFilter(properties, pathMatcher));
|
||||||
bean.setOrder(Integer.MAX_VALUE);
|
bean.setOrder(Integer.MAX_VALUE);
|
||||||
|
@ -27,4 +32,19 @@ public class XssConfiguration {
|
||||||
|
|
||||||
return bean;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package net.maku.framework.common.xss;
|
package net.maku.framework.common.xss;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.util.PathMatcher;
|
import org.springframework.util.PathMatcher;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
@ -29,6 +31,12 @@ public class XssFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean shouldNotFilter(HttpServletRequest request) {
|
protected boolean shouldNotFilter(HttpServletRequest request) {
|
||||||
|
// 如果是json数据,则不处理
|
||||||
|
String contentType = request.getContentType();
|
||||||
|
if (StrUtil.isBlank(contentType) || StrUtil.startWithIgnoreCase(contentType, MediaType.APPLICATION_JSON_VALUE)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// 放行不过滤的URL
|
// 放行不过滤的URL
|
||||||
return properties.getExcludeUrls().stream().anyMatch(excludeUrl -> pathMatcher.match(excludeUrl, request.getRequestURI()));
|
return properties.getExcludeUrls().stream().anyMatch(excludeUrl -> pathMatcher.match(excludeUrl, request.getRequestURI()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
* <a href="https://maku.net">MAKU</a>
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class XssFilterJsonDeserializer extends JsonDeserializer<String> {
|
||||||
|
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<String> handledType() {
|
||||||
|
return String.class;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,9 @@
|
||||||
package net.maku.framework.common.xss;
|
package net.maku.framework.common.xss;
|
||||||
|
|
||||||
import cn.hutool.core.io.IoUtil;
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import jakarta.servlet.ReadListener;
|
|
||||||
import jakarta.servlet.ServletInputStream;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletRequestWrapper;
|
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.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -28,41 +21,6 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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) {
|
public String getParameter(String name) {
|
||||||
String value = super.getParameter(name);
|
String value = super.getParameter(name);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user