注意:

这个xss过滤器有一些问题,比如某些时候,使用jquery ajax post的时候,如果是传的默认的 "application/x-www-form-urlencoded",会出现Controller中的bean接收的值为null的情况,

当我调整ajax设置 contentType:"application/json",dataType:"json" 发送一个带post json数据时,xss拦截器报错,于是删除整个xss包,不再使用这四个类,jquery ajax post一切正常,可以以默认方式提交。(之前只能用axios去拼接参数到url里来实现post(将拼接的参数存入一个变量))

 

总共4个类,放在config.safe.xss包下了:

package org.kosoku.commonfast.config.safe.xss;

import javax.servlet.ServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;

public class HttpGetBody {
/**
* 获取请求Body
*
* @param request
* @return
*/
public static String getBodyString(ServletRequest request) {
//StringBuilder sb = new StringBuilder();
StringBuffer sb = new StringBuffer();
InputStream inputStream = null;
BufferedReader reader = null;
try {
inputStream = request.getInputStream();
reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
String line = "";
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
}

 

package org.kosoku.commonfast.config.safe.xss;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class XssFilter implements Filter {

@Override
public void init(FilterConfig config) throws ServletException {
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
XssHttpServletRequestWrapper xssHttpServletRequestWrapper = new XssHttpServletRequestWrapper((HttpServletRequest) request);
chain.doFilter(xssHttpServletRequestWrapper, response);
}

@Override
public void destroy() {
}
}

 

package org.kosoku.commonfast.config.safe.xss;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;

@Configuration
public class XSSFilterConfig {

@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(xssFilter());
registration.addUrlPatterns("/*");
registration.addInitParameter("paramName", "paramValue");
registration.setName("xssFilter");
return registration;
}

/**
* 创建一个bean
*
* @return
*/
@Bean(name = "xssFilter")
public Filter xssFilter() {
return new XssFilter();
}
}

 

package org.kosoku.commonfast.config.safe.xss;

import org.apache.commons.lang.StringUtils;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
HttpServletRequest orgRequest = null;

private String body;

public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
orgRequest = request;
body = HttpGetBody.getBodyString(request);
}

/**
* 覆盖getParameter方法,将参数名和参数值都做xss过滤。<br/>
* 如果需要获得原始的值,则通过super.getParameterValues(name)来获取<br/>
* getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
*/
@Override
public String getParameter(String name) {
String value = super.getParameter(xssEncode(name, 0));
if (null != value) {
value = xssEncode(value, 0);
}
return value;
}

@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(xssEncode(name, 0));
if (values == null) {
return null;
}
int count = values.length;
String[] encodedValues = new String[count];
for (int i = 0; i < count; i++) {
encodedValues[i] = xssEncode(values[i], 0);
}
return encodedValues;
}

@Override
public Map getParameterMap() {

HashMap paramMap = (HashMap) super.getParameterMap();
paramMap = (HashMap) paramMap.clone();

for (Iterator iterator = paramMap.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry entry = (Map.Entry) iterator.next();
String[] values = (String[]) entry.getValue();
for (int i = 0; i < values.length; i++) {
if (values[i] instanceof String) {
values[i] = xssEncode(values[i], 0);
}
}
entry.setValue(values);
}
return paramMap;
}


@Override
public ServletInputStream getInputStream() throws IOException {
ServletInputStream inputStream = null;
if (StringUtils.isNotEmpty(body)) {
body = xssEncode(body, 1);
InputStream is = new ByteArrayInputStream(body.getBytes("UTF-8"));
inputStream = (ServletInputStream) is;
}
return inputStream;
}

/**
* 覆盖getHeader方法,将参数名和参数值都做xss过滤。<br/>
* 如果需要获得原始的值,则通过super.getHeaders(name)来获取<br/>
* getHeaderNames 也可能需要覆盖
*/
@Override
public String getHeader(String name) {
String value = super.getHeader(xssEncode(name, 0));
if (value != null) {
value = xssEncode(value, 0);
}
return value;
}

/**
* 将容易引起xss漏洞的半角字符直接替换成全角字符
*
* @param s
* @return
*/
private static String xssEncode(String s, int type) {
if (s == null || s.isEmpty()) {
return s;
}
StringBuilder sb = new StringBuilder(s.length() + 16);
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (type == 0) {
switch (c) {
case '\'':
// 全角单引号
sb.append('‘');
break;
case '\"':
// 全角双引号
sb.append('“');
break;
case '>':
// 全角大于号
sb.append('>');
break;
case '<':
// 全角小于号
sb.append('<');
break;
case '&':
// 全角&符号
sb.append('&');
break;
case '\\':
// 全角斜线
sb.append('\');
break;
case '#':
// 全角井号
sb.append('#');
break;
// < 字符的 URL 编码形式表示的 ASCII 字符(十六进制格式) 是: %3c
case '%':
processUrlEncoder(sb, s, i);
break;
default:
sb.append(c);
break;
}
} else {
switch (c) {
case '>':
// 全角大于号
sb.append('>');
break;
case '<':
// 全角小于号
sb.append('<');
break;
case '&':
// 全角&符号
sb.append('&');
break;
case '\\':
// 全角斜线
sb.append('\');
break;
case '#':
// 全角井号
sb.append('#');
break;
// < 字符的 URL 编码形式表示的 ASCII 字符(十六进制格式) 是: %3c
case '%':
processUrlEncoder(sb, s, i);
break;
default:
sb.append(c);
break;
}
}

}
return sb.toString();
}

public static void processUrlEncoder(StringBuilder sb, String s, int index) {
if (s.length() >= index + 2) {
// %3c, %3C
if (s.charAt(index + 1) == '3' && (s.charAt(index + 2) == 'c' || s.charAt(index + 2) == 'C')) {
sb.append('<');
return;
}
// %3c (0x3c=60)
if (s.charAt(index + 1) == '6' && s.charAt(index + 2) == '0') {
sb.append('<');
return;
}
// %3e, %3E
if (s.charAt(index + 1) == '3' && (s.charAt(index + 2) == 'e' || s.charAt(index + 2) == 'E')) {
sb.append('>');
return;
}
// %3e (0x3e=62)
if (s.charAt(index + 1) == '6' && s.charAt(index + 2) == '2') {
sb.append('>');
return;
}
}
sb.append(s.charAt(index));
}

/**
* 获取最原始的request
*
* @return
*/
public HttpServletRequest getOrgRequest() {
return orgRequest;
}

/**
* 获取最原始的request的静态方法
*
* @return
*/
public static HttpServletRequest getOrgRequest(HttpServletRequest req) {
if (req instanceof XssHttpServletRequestWrapper) {
return ((XssHttpServletRequestWrapper) req).getOrgRequest();
}
return req;
}
}