| @@ -6,6 +6,9 @@ import com.fasterxml.jackson.databind.DeserializationFeature; | |||||
| import com.fasterxml.jackson.databind.ObjectMapper; | import com.fasterxml.jackson.databind.ObjectMapper; | ||||
| import com.fasterxml.jackson.databind.module.SimpleModule; | import com.fasterxml.jackson.databind.module.SimpleModule; | ||||
| import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; | import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; | ||||
| import com.iformall.interceptor.HttpServletRequestWrapperFilter; | |||||
| import org.springframework.boot.web.servlet.FilterRegistrationBean; | |||||
| import org.springframework.context.annotation.Bean; | |||||
| import org.springframework.context.annotation.Configuration; | import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.http.converter.HttpMessageConverter; | import org.springframework.http.converter.HttpMessageConverter; | ||||
| import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; | ||||
| @@ -75,5 +78,12 @@ public class WebConfig implements WebMvcConfigurer { | |||||
| converters.add(jackson2HttpMessageConverter); | converters.add(jackson2HttpMessageConverter); | ||||
| } | } | ||||
| @Bean | |||||
| public FilterRegistrationBean<HttpServletRequestWrapperFilter> Filters() { | |||||
| FilterRegistrationBean<HttpServletRequestWrapperFilter> registrationBean = new FilterRegistrationBean<HttpServletRequestWrapperFilter>(); | |||||
| registrationBean.setFilter(new HttpServletRequestWrapperFilter()); | |||||
| registrationBean.addUrlPatterns("/*"); | |||||
| registrationBean.setName("koalaSignFilter"); | |||||
| return registrationBean; | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,76 @@ | |||||
| package com.iformall.interceptor; | |||||
| import javax.servlet.ReadListener; | |||||
| import javax.servlet.ServletInputStream; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import java.io.*; | |||||
| public class BodyReaderHttpServletRequestWrapper extends XssHttpServletRequestWrapper { | |||||
| private final String body; | |||||
| public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException { | |||||
| super(request); | |||||
| StringBuffer stringBuilder = new StringBuffer(); | |||||
| BufferedReader bufferedReader = null; | |||||
| try { | |||||
| InputStream inputStream = request.getInputStream(); | |||||
| if (inputStream != null) { | |||||
| bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"utf-8")); | |||||
| char[] charBuffer = new char[1024]; | |||||
| int bytesRead = -1; | |||||
| while ((bytesRead = bufferedReader.read(charBuffer)) > 0) { | |||||
| stringBuilder.append(charBuffer, 0, bytesRead); | |||||
| } | |||||
| } else { | |||||
| stringBuilder.append(""); | |||||
| } | |||||
| } catch (IOException ex) { | |||||
| throw ex; | |||||
| } finally { | |||||
| if (bufferedReader != null) { | |||||
| try { | |||||
| bufferedReader.close(); | |||||
| } catch (IOException ex) { | |||||
| throw ex; | |||||
| } | |||||
| } | |||||
| } | |||||
| body = stringBuilder.toString(); | |||||
| } | |||||
| @Override | |||||
| public ServletInputStream getInputStream() throws IOException { | |||||
| final ByteArrayInputStream byteArrayInputStream = | |||||
| new ByteArrayInputStream(body.getBytes("utf-8")); | |||||
| return new ServletInputStream() { | |||||
| @Override | |||||
| public boolean isFinished() { | |||||
| return false; | |||||
| } | |||||
| @Override | |||||
| public boolean isReady() { | |||||
| return false; | |||||
| } | |||||
| @Override | |||||
| public void setReadListener(ReadListener readListener) { | |||||
| } | |||||
| @Override | |||||
| public int read() throws IOException { | |||||
| return byteArrayInputStream.read(); | |||||
| } | |||||
| }; | |||||
| } | |||||
| @Override | |||||
| public BufferedReader getReader() throws IOException { | |||||
| return new BufferedReader(new InputStreamReader(this.getInputStream())); | |||||
| } | |||||
| public String getBody() { | |||||
| return this.body; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,70 @@ | |||||
| package com.iformall.interceptor; | |||||
| import com.iformall.utils.UrlCheck; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import javax.servlet.*; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import java.io.IOException; | |||||
| public class HttpServletRequestWrapperFilter implements Filter { | |||||
| private static final Logger logger = LoggerFactory.getLogger(HttpServletRequestWrapperFilter.class); | |||||
| // 多个跨域域名设置 | |||||
| // public static final String[] ALLOW_DOMAIN = {"https://admin.malls.iformall.com"}; | |||||
| @Override | |||||
| public void init(FilterConfig filterConfig) throws ServletException { | |||||
| } | |||||
| @Override | |||||
| public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) | |||||
| throws IOException, ServletException { | |||||
| ServletRequest requestWrapper = null; | |||||
| /* // 跨域访问 | |||||
| HttpServletRequest req = (HttpServletRequest) request; | |||||
| HttpServletResponse res = (HttpServletResponse) response; | |||||
| String originHeader = req.getHeader("Origin"); | |||||
| if (Arrays.asList(ALLOW_DOMAIN).contains(originHeader)) { | |||||
| //通过在响应 header 中设置 ‘*’ 来允许来自所有域的跨域请求访问。 | |||||
| res.setHeader("Access-Control-Allow-Origin", originHeader); | |||||
| //通过对 Credentials 参数的设置,就可以保持跨域 Ajax 时的 Cookie | |||||
| //设置了Allow-Credentials,Allow-Origin就不能为*,需要指明具体的url域 | |||||
| res.setHeader("Access-Control-Allow-Credentials", "true"); | |||||
| //请求方式 | |||||
| res.setHeader("Access-Control-Allow-Methods", "*"); | |||||
| //(预检请求)的返回结果(即 Access-Control-Allow-Methods 和Access-Control-Allow-Headers 提供的信息) 可以被缓存多久 | |||||
| res.setHeader("Access-Control-Max-Age", "86400"); | |||||
| //首部字段用于预检请求的响应。其指明了实际请求中允许携带的首部字段 | |||||
| //res.setHeader("Access-Control-Allow-Headers", "*"); | |||||
| res.setHeader("Access-Control-Allow-Headers", | |||||
| "Timestamp,Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token,Access-Control-Allow-Headers"); | |||||
| } | |||||
| */ | |||||
| //sql,xss过滤 | |||||
| XssHttpServletRequestWrapper xssHttpServletRequestWrapper = new XssHttpServletRequestWrapper((HttpServletRequest)request); | |||||
| if (request instanceof HttpServletRequest) { | |||||
| String url = ((HttpServletRequest) request).getRequestURI(); | |||||
| if (!UrlCheck.checkUrl(url)) { | |||||
| requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request); | |||||
| } | |||||
| } | |||||
| if (null == requestWrapper) { | |||||
| chain.doFilter(xssHttpServletRequestWrapper, response); | |||||
| } else { | |||||
| chain.doFilter(requestWrapper, response); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void destroy() { | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,122 @@ | |||||
| package com.iformall.interceptor; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.util.StringUtils; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import javax.servlet.http.HttpServletRequestWrapper; | |||||
| import java.util.HashMap; | |||||
| import java.util.HashSet; | |||||
| import java.util.Map; | |||||
| import java.util.Set; | |||||
| /** | |||||
| * 防止sql注入,xss攻击 | |||||
| * 前端可以对输入信息做预处理,后端也可以做处理。 | |||||
| */ | |||||
| public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { | |||||
| private final Logger log = LoggerFactory.getLogger(getClass()); | |||||
| private static String key = "and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+"; | |||||
| private static Set<String> notAllowedKeyWords = new HashSet<String>(0); | |||||
| private static String replacedString="INVALID"; | |||||
| static { | |||||
| String keyStr[] = key.split("\\|"); | |||||
| for (String str : keyStr) { | |||||
| notAllowedKeyWords.add(str); | |||||
| } | |||||
| } | |||||
| private String currentUrl; | |||||
| public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) { | |||||
| super(servletRequest); | |||||
| currentUrl = servletRequest.getRequestURI(); | |||||
| } | |||||
| /**覆盖getParameter方法,将参数名和参数值都做xss过滤。 | |||||
| * 如果需要获得原始的值,则通过super.getParameterValues(name)来获取 | |||||
| * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖 | |||||
| */ | |||||
| @Override | |||||
| public String getParameter(String parameter) { | |||||
| String value = super.getParameter(parameter); | |||||
| if (value == null) { | |||||
| return null; | |||||
| } | |||||
| return cleanXSS(value); | |||||
| } | |||||
| @Override | |||||
| public String[] getParameterValues(String parameter) { | |||||
| String[] values = super.getParameterValues(parameter); | |||||
| if (values == null) { | |||||
| return null; | |||||
| } | |||||
| int count = values.length; | |||||
| String[] encodedValues = new String[count]; | |||||
| for (int i = 0; i < count; i++) { | |||||
| encodedValues[i] = cleanXSS(values[i]); | |||||
| } | |||||
| return encodedValues; | |||||
| } | |||||
| @Override | |||||
| public Map<String, String[]> getParameterMap(){ | |||||
| Map<String, String[]> values=super.getParameterMap(); | |||||
| if (values == null) { | |||||
| return null; | |||||
| } | |||||
| Map<String, String[]> result=new HashMap<>(); | |||||
| for(String key:values.keySet()){ | |||||
| String encodedKey=cleanXSS(key); | |||||
| int count=values.get(key).length; | |||||
| String[] encodedValues = new String[count]; | |||||
| for (int i = 0; i < count; i++){ | |||||
| encodedValues[i]=cleanXSS(values.get(key)[i]); | |||||
| } | |||||
| result.put(encodedKey,encodedValues); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| /** | |||||
| * 覆盖getHeader方法,将参数名和参数值都做xss过滤。 | |||||
| * 如果需要获得原始的值,则通过super.getHeaders(name)来获取 | |||||
| * getHeaderNames 也可能需要覆盖 | |||||
| */ | |||||
| @Override | |||||
| public String getHeader(String name) { | |||||
| String value = super.getHeader(name); | |||||
| if (value == null) { | |||||
| return null; | |||||
| } | |||||
| return cleanXSS(value); | |||||
| } | |||||
| private String cleanXSS(String valueP) { | |||||
| // You'll need to remove the spaces from the html entities below | |||||
| String value = valueP.replaceAll("<", "<").replaceAll(">", ">"); | |||||
| value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;"); | |||||
| value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;"); | |||||
| value = value.replaceAll("'", "& #39;"); | |||||
| value = value.replaceAll("eval\\((.*)\\)", ""); | |||||
| value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\""); | |||||
| value = value.replaceAll("script", ""); | |||||
| value = cleanSqlKeyWords(value); | |||||
| return value; | |||||
| } | |||||
| private String cleanSqlKeyWords(String value) { | |||||
| String paramValue = value; | |||||
| for (String keyword : notAllowedKeyWords) { | |||||
| if (paramValue.length() > keyword.length() + 4 | |||||
| && (paramValue.contains(" "+keyword)||paramValue.contains(keyword+" ")||paramValue.contains(" "+keyword+" "))) { | |||||
| paramValue = StringUtils.replace(paramValue, keyword, replacedString); | |||||
| log.error(this.currentUrl + "已被过滤,因为参数中包含不允许sql的关键词(" + keyword | |||||
| + ")"+";参数:"+value+";过滤后的参数:"+paramValue); | |||||
| } | |||||
| } | |||||
| return paramValue; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,15 @@ | |||||
| package com.iformall.utils; | |||||
| /** | |||||
| * @author gongbiao | |||||
| */ | |||||
| public class UrlCheck { | |||||
| public static boolean checkUrl(String url) { | |||||
| return url.contains("awsFileUpload") | |||||
| || url.contains("awsFilesUpload") | |||||
| || url.contains("wxMsgCallback") | |||||
| || url.contains("notify"); | |||||
| } | |||||
| } | |||||