新增XSS过滤
This commit is contained in:
parent
ddcdc844dd
commit
e7bbef4d43
|
@ -0,0 +1,29 @@
|
|||
package net.maku.framework.common.xss;
|
||||
|
||||
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.util.PathMatcher;
|
||||
|
||||
/**
|
||||
* XSS 配置文件
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(XssProperties.class)
|
||||
@ConditionalOnProperty(prefix = "maku.xss", value = "enabled")
|
||||
public class XssConfiguration {
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean<XssFilter> xssFilter(XssProperties properties, PathMatcher pathMatcher) {
|
||||
FilterRegistrationBean<XssFilter> bean = new FilterRegistrationBean<>();
|
||||
bean.setFilter(new XssFilter(properties, pathMatcher));
|
||||
bean.setOrder(Integer.MAX_VALUE);
|
||||
bean.setName("xssFilter");
|
||||
|
||||
return bean;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package net.maku.framework.common.xss;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Xss 过滤器
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class XssFilter extends OncePerRequestFilter {
|
||||
private final XssProperties properties;
|
||||
private final PathMatcher pathMatcher;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws IOException, ServletException {
|
||||
filterChain.doFilter(new XssRequestWrapper(request), response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldNotFilter(HttpServletRequest request) {
|
||||
// 放行不过滤的URL
|
||||
return properties.getExcludeUrls().stream().anyMatch(excludeUrl -> pathMatcher.match(excludeUrl, request.getRequestURI()));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package net.maku.framework.common.xss;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* XSS 配置项
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "maku.xss")
|
||||
public class XssProperties {
|
||||
/**
|
||||
* 是否开启 XSS
|
||||
*/
|
||||
private boolean enabled;
|
||||
/**
|
||||
* 排除的URL列表
|
||||
*/
|
||||
private List<String> excludeUrls = Collections.emptyList();
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package net.maku.framework.common.xss;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import javax.servlet.ReadListener;
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* XSS Request Wrapper
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
public class XssRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
public XssRequestWrapper(HttpServletRequest request) {
|
||||
super(request);
|
||||
}
|
||||
|
||||
@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(content.getBytes());
|
||||
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);
|
||||
|
||||
return filterXss(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getParameterValues(String name) {
|
||||
String[] parameters = super.getParameterValues(name);
|
||||
if (parameters == null || parameters.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
parameters[i] = filterXss(parameters[i]);
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String[]> getParameterMap() {
|
||||
Map<String, String[]> map = new LinkedHashMap<>();
|
||||
Map<String, String[]> parameters = super.getParameterMap();
|
||||
for (String key : parameters.keySet()) {
|
||||
String[] values = parameters.get(key);
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
values[i] = filterXss(values[i]);
|
||||
}
|
||||
map.put(key, values);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeader(String name) {
|
||||
String value = super.getHeader(name);
|
||||
return filterXss(value);
|
||||
}
|
||||
|
||||
private String filterXss(String content) {
|
||||
if (StrUtil.isBlank(content)) {
|
||||
return content;
|
||||
}
|
||||
|
||||
return XssUtils.filter(content);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package net.maku.framework.common.xss;
|
||||
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.http.HTMLFilter;
|
||||
|
||||
|
||||
/**
|
||||
* XSS 过滤工具类
|
||||
*
|
||||
* @author 阿沐 babamu@126.com
|
||||
*/
|
||||
public class XssUtils {
|
||||
private static final ThreadLocal<HTMLFilter> HTML_FILTER = ThreadLocal.withInitial(() -> {
|
||||
HTMLFilter htmlFilter = new HTMLFilter();
|
||||
// 避免 " 被转成 " 字符
|
||||
ReflectUtil.setFieldValue(htmlFilter, "encodeQuotes", false);
|
||||
return htmlFilter;
|
||||
});
|
||||
|
||||
/**
|
||||
* XSS过滤
|
||||
*
|
||||
* @param content 需要过滤的内容
|
||||
* @return 返回过滤后的内容
|
||||
*/
|
||||
public static String filter(String content) {
|
||||
return HTML_FILTER.get().filter(content);
|
||||
}
|
||||
|
||||
}
|
|
@ -30,6 +30,10 @@ storage:
|
|||
local:
|
||||
path: D://upload
|
||||
|
||||
maku:
|
||||
xss:
|
||||
enabled: true
|
||||
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath*:/mapper/**/*.xml
|
||||
# 实体扫描,多个package用逗号或者分号分隔
|
||||
|
|
Loading…
Reference in New Issue
Block a user