前言
在springboot配置过滤实现方案有两种, 一种是基于serlvet 的注解 @WebFilter 进行配置,一种是使用Springboot提供的 FilterRegistrationBean注册自定义过滤器。
该篇使用的方案是后者,因为按照我以前使用的记忆里,这种方式可以避免一些偶然出现的小问题,如:过滤器没生效;生效后url匹配不生效等。
正文
在开始敲代码前,先从上帝视角看看我们这次实践案例,做了些什么:
BodyReaderHttpServletRequestWrapper
名字显然是随便取的, 但是从字面意义来看,就是关于body内容的读取。
为什么要写一个这样的东西?
简单讲讲:
@RequestBody 这个注解大家并不陌生,post请求里,规定参数传递使用application/json 流数据传递(序列化后的json字符串)。
正因为这个请求体重的流数据,流数据只能读取一次。
而我们这次实践案例中,过滤器读取一次,接口还需要读取一次, 如果不整点手法,那么这个流数据明显不够用。
因此, 我们采取了 继承HttpServletRequestWrapper ,创建 BodyReaderHttpServletRequestWrapper 来
将流数据进行复制存储起来。当无论第一次第二次需要使用到流数据时 ,都去当前存储起来的body数据里去读取。
上代码,新建 BodyReaderHttpServletRequestWrapper.java :
import org.apache.commons.lang3.StringUtils; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.Enumeration; import java.util.Map; import java.util.Vector; /** * @Author : JCccc * @CreateTime : 2020/3/27 * @Description : **/ public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper { private final byte[] body; /** * 所有参数的集合 */ private Map<String, String[]> parameterMap; public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException { super(request); BufferedReader reader = request.getReader(); body = readBytes(reader); parameterMap = request.getParameterMap(); } @Override public BufferedReader getReader() throws IOException { ServletInputStream inputStream = getInputStream(); if (null == inputStream) { return null; } return new BufferedReader(new InputStreamReader(inputStream)); } @Override public Enumeration<String> getParameterNames() { Vector<String> vector = new Vector<>(parameterMap.keySet()); return vector.elements(); } @Override public ServletInputStream getInputStream() throws IOException { if (body == null) { return null; } final ByteArrayInputStream bais = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener listener) { } @Override public int read() throws IOException { return bais.read(); } }; } /** * 通过BufferedReader和字符编码集转换成byte数组 * * @param br * @return * @throws IOException */ private byte[] readBytes(BufferedReader br) throws IOException { String str; StringBuilder retStr = new StringBuilder(); while ((str = br.readLine()) != null) { retStr.append(str); } if (StringUtils.isNotBlank(retStr.toString())) { return retStr.toString().getBytes(StandardCharsets.UTF_8); } return null; } }
接着,自定义 第一个过滤器 , CheckUserFilter.java:
import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.BufferedReader; import java.io.IOException; /** * @Author : JCccc * @CreateTime : 2020/3/27 * @Description : **/ public class CheckUserFilter implements Filter { @Override public void init(FilterConfig filterConfig) { System.out.println("过滤器一初始化"); } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException { System.out.println("进入到第一个过滤器,执行相关逻辑处理"); HttpServletRequest request = (HttpServletRequest) req; String path = request.getRequestURI(); String method = request.getMethod(); System.out.println(method); //排除一些url的拦截 if (path.equals("/test/testContext")) { filterChain.doFilter(request, res); } if ("POST".equals(method)) { BodyReaderHttpServletRequestWrapper requestWrapper = new BodyReaderHttpServletRequestWrapper(request); // 从Request的包装类中读取数据 BufferedReader reader = requestWrapper.getReader(); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line); } reader.close(); System.out.println(sb.toString()); filterChain.doFilter(requestWrapper, res); } } @Override public void destroy() { System.out.println("过滤器一销毁了"); } }
然后再自定义一个过滤器,CheckUserFilterNext.java :
import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.BufferedReader; import java.io.IOException; /** * @Author : JCccc * @CreateTime : 2020/3/27 * @Description : **/ public class CheckUserFilterNext implements Filter { @Override public void init(FilterConfig filterConfig) { System.out.println("过滤器二初始化"); } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException { System.out.println("进入到第二个过滤器,执行相关逻辑处理"); HttpServletRequest request = (HttpServletRequest) req; String path = request.getRequestURI(); String method = request.getMethod(); System.out.println(method); //排除一些url的拦截 if (path.equals("/test/testContext")) { filterChain.doFilter(request, res); } if ("POST".equals(method)) { BodyReaderHttpServletRequestWrapper requestWrapper = new BodyReaderHttpServletRequestWrapper(request); // 从Request的包装类中读取数据 BufferedReader reader = requestWrapper.getReader(); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line); } reader.close(); System.out.println(sb.toString()); filterChain.doFilter(requestWrapper, res); } } @Override public void destroy() { System.out.println("过滤器二销毁了"); } }
然后是将这两个过滤器都丢进spring容器里面去,顺便配置一些 拦截的url和执行顺序(毕竟是两个过滤器,肯定有执行顺序):
那么我们来到 application加上相关代码:
/** * 第一个过滤器配置 * */ @Bean CheckUserFilter getCheckUserFilter(){ return new CheckUserFilter(); } @Bean("checkUserFilter") public FilterRegistrationBean<CheckUserFilter> checkUserFilter(CheckUserFilter checkUserFilter) { FilterRegistrationBean<CheckUserFilter> registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(checkUserFilter); registrationBean.addUrlPatterns("/test/*"); //url拦截 registrationBean.setOrder(1); registrationBean.setAsyncSupported(true); return registrationBean; } /** * 第二个过滤器配置 * */ @Bean CheckUserFilterNext getCheckUserFilterNext(){ return new CheckUserFilterNext(); } @Bean("checkUserFilterNext") public FilterRegistrationBean<CheckUserFilterNext> checkUserFilterNext(CheckUserFilterNext checkUserFilterNext) { FilterRegistrationBean<CheckUserFilterNext> registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(checkUserFilterNext); registrationBean.addUrlPatterns("/test/*"); //url拦截 registrationBean.setOrder(2); registrationBean.setAsyncSupported(true); return registrationBean; }
若想要配置第三个过滤器,那么也是一样,自定义一个过滤器继承Filter,然后再一样注册到application里面去。
接下来我们开始写点接口去测试一下,
新建一个 MyTestController.java :
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; /** * @Author : JCccc * @CreateTime : 2020/3/27 * @Description : **/ @Controller @RequestMapping("/test") public class MyTestController { @ResponseBody @RequestMapping(value="testFilter",method={RequestMethod.POST}) public void testFilter(@RequestBody String jsonStr) { System.out.println("aaaaa"); System.out.println(jsonStr); } }
项目跑起来,可以看到:
咱们刚刚配置的过滤器都已经初始化准备好了,
接下来我们调用一下测试接口:
直接看结果:
ok,过滤器的使用就暂且到这吧。