首先我们要创建一个执行转义的封装类,这个类继承 HttpservletRequestwrapper 父类。在Web项目中,我们无法修改 HttpservletRequest 实现类的内容,因为请求的实现类是由各个Web容器厂商自己扩展的。但是有时候我们还想修改请求类中的内容,这该怎么办呢?Java语言给我们留出了缺口,我们只要继承Java Web内置的HttpservletRequestwrapper 父类,就能修改请求类的内容。如果我们能修改请求类的内容,我要修改获取请求数据的函数,返回的并不是客户端Form表单或者Ajax提交的数据,而是经过转义之后数据。
在 com.jiang.his.config.xss 包中创建 XssHttpServletRequestwrapper 类。把获取请求头和请求体数据的方法都要重写,返回的是经过XSS转义后的数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
| package com.jiang.his.config.xss;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import cn.hutool.core.util.StrUtil; import cn.hutool.http.HtmlUtil; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletInputStream; import javax.servlet.ReadListener; import java.io.*; import org.apache.commons.io.IOUtils; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil;
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { private final byte[] body; public XssHttpServletRequestWrapper(HttpServletRequest request) throws IOException { super(request); try (InputStream in = request.getInputStream()) { body = IOUtils.toByteArray(in); } }
@Override public String getHeader(String name) { String value = super.getHeader(name); return escapeXss(value); }
@Override public String getParameter(String name) { String value = super.getParameter(name); return escapeXss(value); }
@Override public String[] getParameterValues(String name) { String[] values = super.getParameterValues(name); if (values == null) { return null; } String[] escapeValues = new String[values.length]; for (int i = 0; i < values.length; i++) { escapeValues[i] = escapeXss(values[i]); } return escapeValues; }
@Override public Map<String, String[]> getParameterMap() { Map<String, String[]> parameterMap = super.getParameterMap(); Map<String, String[]> escapedParameterMap = new HashMap<>(); for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) { String[] values = entry.getValue(); String[] escapedValues = new String[values.length]; for (int i = 0; i < values.length; i++) { escapedValues[i] = escapeXss(values[i]); } escapedParameterMap.put(entry.getKey(), escapedValues); } return escapedParameterMap; } @Override public ServletInputStream getInputStream() throws IOException { if (body == null || body.length == 0) { return super.getInputStream(); }
String bodyString = new String(body); try { JSONObject jsonObject = JSONUtil.parseObj(bodyString); cleanXssJson(jsonObject); bodyString = jsonObject.toString(); } catch (Exception e) { bodyString = escapeXss(bodyString); }
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bodyString.getBytes());
return new ServletInputStream() { @Override public boolean isFinished() { return false; }
@Override public boolean isReady() { return true; }
@Override public void setReadListener(ReadListener readListener) { }
@Override public int read() { return byteArrayInputStream.read(); } }; }
private void cleanXssJson(JSONObject jsonObject) { for (String key : jsonObject.keySet()) { Object value = jsonObject.get(key); if (value instanceof String) { jsonObject.set(key, escapeXss((String) value)); } else if (value instanceof JSONObject) { cleanXssJson((JSONObject) value); } } }
private String escapeXss(String value) { if (StrUtil.isBlank(value)) { return value; } return HtmlUtil.cleanHtmlTag(value); } }
|
在 com.jiang.his.config.xss 包中,创建 XssFilter 类。把 HttpServletRequest 包装成 XssHttpServletRequestWrapper,然后传给Web方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| package com.jiang.his.config.xss;
import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern;
public class XssFilter implements Filter { private List<String> excludes = new ArrayList<>(); @Override public void init(FilterConfig filterConfig) throws ServletException { String temp = filterConfig.getInitParameter("excludes"); if (temp != null) { String[] url = temp.split(","); for (int i = 0; i < url.length; i++) { excludes.add(url[i]); } } }
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; if (handleExcludeURL(httpRequest)) { chain.doFilter(request, response); return; } if (isMultipartContent(httpRequest)) { chain.doFilter(request, response); return; } XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(httpRequest); chain.doFilter(xssRequest, response); } private boolean handleExcludeURL(HttpServletRequest request) { if (excludes == null || excludes.isEmpty()) { return false; } String url = request.getServletPath(); for (String pattern : excludes) { Pattern p = Pattern.compile("^" + pattern); Matcher m = p.matcher(url); if (m.find()) { return true; } } return false; } private boolean isMultipartContent(HttpServletRequest request) { String contentType = request.getContentType(); return contentType != null && contentType.toLowerCase().startsWith("multipart/"); }
@Override public void destroy() { } }
|
在 com.jiang.his.config.xss 包中,创建 XssConfig 类。在类中,创建一个过滤器注册方法,把 XssFilter 注册到过滤器链中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package com.jiang.his.config.xss;
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
@Configuration public class XssConfig {
@Bean public FilterRegistrationBean<XssFilter> xssFilterRegistrationBean() { FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>(); registration.setFilter(new XssFilter()); registration.addUrlPatterns("/*"); registration.setName("XssFilter"); registration.setOrder(1); return registration; } }
|
允许跨域访问CORS
在 com,jiang.his.config包中创建 CorsConfig.java类,扩展 WebMvcConfigurer 接囗。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.jiang.his.config;
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration public class CorsConfig implements WebMvcConfigurer {
@Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600L); } }
|