需求:去除用户表单参数中由于用户不小心输入的前后空格,防止因为前后空格原因引起业务异常
实现方式一:前端参数传入的时候去除首尾空格
实现方式二:后端接收参数对参数处理,去除参数首尾空格后再做其他业务
实现方式三:利用Filter处理所有的请求,去除请求参数首尾空格重新写回
很明显实现方式一和实现方式二代码维护严重,需要对每个参数进行手动的去除首尾空格,这边主要讲实现方式三,这边实现方式用的是spring boot框架,需要对这个框架或者spring有一个简单的了解,文章后边有项目demo下载地址
1,注册过滤器
/** * 去除参数头尾空格过滤器 * @return */ @Bean public FilterRegistrationBean parmsFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setDispatcherTypes(DispatcherType.REQUEST); registration.setFilter(new ParamsFilter()); registration.addUrlPatterns("/*"); registration.setName("paramsFilter"); registration.setOrder(Integer.MAX_VALUE-1); return registration; }
2,ParamsFilter实现Filter类
public class ParamsFilter implements Filter{ @Override public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException { ParameterRequestWrapper parmsRequest = new ParameterRequestWrapper( (HttpServletRequest) arg0); arg2.doFilter(parmsRequest, arg1); } @Override public void init(FilterConfig arg0) throws ServletException { } @Override public void destroy() { } }
3,ParameterRequestWrapper类,也是本节实现最最重要的一个类,继承HttpServletRequestWrapper重写它的getParameter,getParameterValues方法,还有一个比较重要的就是必须重写getInputStream方法,因为如果请求以enctype=”application/x-www-form-urlencoded”方式提交的时候getParameterMap可以获取到表单提交的数据,而请求以enctype="multipart/form-data"方式提交的话是获取不到数据的,这个时候就必须从getInputStream流中获取,后面一种也是post常用的类型,也就是为什么用post提交请求没有参数大小的限制的原因了,具体代码如下:
public class ParameterRequestWrapper extends HttpServletRequestWrapper { private Map<String , String[]> params = new HashMap<String, String[]>(); public ParameterRequestWrapper(HttpServletRequest request) { // 将request交给父类,以便于调用对应方法的时候,将其输出,其实父亲类的实现方式和第一种new的方式类似 super(request); //将参数表,赋予给当前的Map以便于持有request中的参数 Map<String, String[]> requestMap=request.getParameterMap(); System.out.println("转化前参数:"+JSON.toJSONString(requestMap)); this.params.putAll(requestMap); this.modifyParameterValues(); System.out.println("转化后参数:"+JSON.toJSONString(params)); } /** * 重写getInputStream方法 post类型的请求参数必须通过流才能获取到值 */ @Override public ServletInputStream getInputStream() throws IOException { //非json类型,直接返回 if(!super.getHeader(HttpHeaders.CONTENT_TYPE).equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE)){ return super.getInputStream(); } //为空,直接返回 String json = IOUtils.toString(super.getInputStream(), "utf-8"); if (StringUtils.isEmpty(json)) { return super.getInputStream(); } System.out.println("转化前参数:"+json); Map<String,Object> map=StringJsonUtils.jsonStringToMap(json); System.out.println("转化后参数:"+JSON.toJSONString(map)); ByteArrayInputStream bis = new ByteArrayInputStream(JSON.toJSONString(map).getBytes("utf-8")); return new MyServletInputStream(bis); } /** * 将parameter的值去除空格后重写回去 */ public void modifyParameterValues(){ Set<String> set =params.keySet(); Iterator<String> it=set.iterator(); while(it.hasNext()){ String key= (String) it.next(); String[] values = params.get(key); values[0] = values[0].trim(); params.put(key, values); } } /** * 重写getParameter 参数从当前类中的map获取 */ @Override public String getParameter(String name) { String[]values = params.get(name); if(values == null || values.length == 0) { return null; } return values[0]; } /** * 重写getParameterValues */ public String[] getParameterValues(String name) {//同上 return params.get(name); } class MyServletInputStream extends ServletInputStream{ private ByteArrayInputStream bis; public MyServletInputStream(ByteArrayInputStream bis){ this.bis=bis; } @Override public boolean isFinished() { return true; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener listener) { } @Override public int read() throws IOException { return bis.read(); } } }
4,启动项目访问:http://localhost/test/get?userName=%20%20%20dd%20%20
结果如下图:
预留一个问题:怎么通过过滤器实现XSS攻击?
PS:本demo项目完整项目路径下载:https://github.com/zqh1989/spring/tree/master/spring-boot-filter-01