当前位置:   article > 正文

SpringBoot添加过滤器Filter_springboot 增加filter

springboot 增加filter

1. 拦截器和过滤器
先说一下,过滤器和拦截器的区别和联系。

1.1 相同点
首先过滤器和拦截器都能实现请求的筛选(过滤或者拦截),然后根据自己的业务需求,添加自己的逻辑,保证请求往后走的时候数据能满足自己的需求。同时呢,他们又都能终止请求流(过滤器只要不在过滤链中往后传request就形;拦截器返回false)。

1.2 不同点
1.2.1 实现原理不同

过滤器和拦截器 底层实现方式大不相同,过滤器 是基于函数回调的,拦截器 则是基于Java的反射机制(动态代理)实现的

1.2.2 使用范围不同
过滤器 实现的是 javax.servlet.Filter 接口,而这个接口是在Servlet规范中定义的,也就是说过滤器Filter 的使用要依赖于Servlet的,生存与Tomcat等服务器容器中,导致它只能在web程序中使用
拦截器(Interceptor) 它是一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于Application、Swing等程序中。

1.2.3 触发时机不同
由于过滤器和拦截器基于不同的容器,所以他们的触发时机和请求中经过容器的顺序有关,Filter作用于Servlet,Interceptor作用于Springmvc。

1.2.4 处理范围不同
过滤器处理逻辑都在doFilter方法中,拦截器区分pre、post和after,划分粒度更细了,使用起来更灵活

                                      过滤器

                                      拦截器

1.2.5 拦截器可以注入业务
拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。

2. Springboot添加拦截器
项目当中使用过滤器还是拦截器,根据需求来定,一般用哪种都可以,我是要处理requestbody 中的数据(处理特殊字符,加密等)。由于数据还要往下继续传,所以选用filter。
过滤器添加有2种方法:

写过滤器类,并实现Filter接口
添加@WebFilter注解,属性filterName设置过滤器名称,urlPatterns匹配要过滤的url

  1. import java.io.IOException;
  2. import javax.servlet.Filter;
  3. import javax.servlet.FilterChain;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.ServletRequest;
  6. import javax.servlet.ServletResponse;
  7. import javax.servlet.annotation.WebFilter;
  8. @WebFilter(filterName = "testFilter", urlPatterns = "/*")
  9. public class TestFilter implements Filter {
  10. @Override
  11. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  12. throws IOException, ServletException {
  13. System.out.println("==过滤逻辑==");
  14. chain.doFilter(request, response);
  15. }
  16. }

在启动类上添加@ServletComponentScan注解,添加这个注解后,@WebServlet、@WebFilter、@WebListener注解都可以被扫描到了。

2.2 使用FilterRegistrationBean对象,将过滤器添加到spring容器,需配合@Configuration和@Bean注解
        a.写过滤器类,实现Filter接口,并添加 @Component注解

  1. import java.io.IOException;
  2. import javax.servlet.Filter;
  3. import javax.servlet.FilterChain;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.ServletRequest;
  6. import javax.servlet.ServletResponse;
  7. import javax.servlet.http.HttpServletRequest;
  8. import org.apache.http.entity.ContentType;
  9. import org.springframework.http.HttpMethod;
  10. import org.springframework.stereotype.Component;
  11. /**
  12. * gateway转发参数时,有特殊符号如 * +等经过了urlencode,无法正确接收,
  13. * 加过滤器将 body进行urldecode
  14. *
  15. * 注意的问题:RequestBody注解的数据,
  16. * 要通过request的getReader和getInputStream()方法来获取流数据,然后转变为字符串的
  17. * 但是流读取一次后就关闭了,在filter中读取了以后,controller中就拿不到数据了
  18. * 为此,我们自定义一个HttpServletRequestWrapper对象,将拿到的数据在放到这个对象中,
  19. * 并将这个对象从chain.doFilter()方法中继续传下去,后续就能继续读取了
  20. */
  21. @Component
  22. public class RequestBodyFilter implements Filter {
  23. @Override
  24. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  25. throws IOException, ServletException {
  26. HttpServletRequest req = (HttpServletRequest) request;
  27. String contextPath = request.getServletContext().getContextPath();
  28. boolean b1 = HttpMethod.POST.name().equals(req.getMethod());
  29. boolean b2 = ContentType.APPLICATION_JSON.getMimeType().equals(request.getContentType());
  30. boolean b3 = request.getParameterMap().isEmpty();
  31. boolean b4 = contextPath.contains("/test");
  32. if (b1 && b2 && b3 && b4) {
  33. MyHttpServletRequestWrapper requestWrapper = new MyHttpServletRequestWrapper(req);
  34. chain.doFilter(requestWrapper, response);
  35. } else {
  36. chain.doFilter(request, response);
  37. }
  38. }
  39. }

        b.将过滤器添加到FilterRegistrationBean,并交给spring容器

  1. import javax.servlet.Filter;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.boot.web.servlet.FilterRegistrationBean;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6. @Configuration
  7. public class MyFilterConfig {
  8. @Autowired
  9. private RequestBodyFilter requestBodyFilter;
  10. /**
  11. * 将过滤器注册到FilterRegistrationBean中
  12. * 进行简单配置
  13. */
  14. @Bean
  15. public FilterRegistrationBean<Filter> someFilterRegistration() {
  16. FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
  17. registration.setFilter(requestBodyFilter);
  18. registration.addUrlPatterns("/*");
  19. registration.setName("requestBodyFilter");
  20. registration.setOrder(99);
  21. return registration;
  22. }
  23. }

踩的坑,因为在过滤器中通过request的getReader和getInputStream()方法来获取了流数据,但是流读取一次后就关闭了,在filter中读取了以后,controller中就拿不到数据了。为此,我们自定义一个HttpServletRequestWrapper对象,将拿到的数据在放到这个对象中, 并将这个对象从chain.doFilter()方法中继续传下去,后续就能继续读取了。

  1. import java.io.BufferedReader;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import java.io.UnsupportedEncodingException;
  6. import java.net.URLDecoder;
  7. import java.nio.CharBuffer;
  8. import java.nio.charset.Charset;
  9. import javax.servlet.ReadListener;
  10. import javax.servlet.ServletInputStream;
  11. import javax.servlet.http.HttpServletRequest;
  12. import javax.servlet.http.HttpServletRequestWrapper;
  13. /**
  14. * 自定义request对象,将body处理后,放到这个对象里面,
  15. * 一定要重写getReader()和getInputStream()方法,将body放进去,
  16. * 这样后面的controller才能从这个request的getReader()和getInputStream()
  17. * 方法中拿到新的body对象
  18. */
  19. public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {
  20. private byte[] body;
  21. public MyHttpServletRequestWrapper(HttpServletRequest request) {
  22. super(request);
  23. final StringBuilder builder = new StringBuilder();
  24. // 缓存按 8192 大小
  25. final CharBuffer buffer = CharBuffer.allocate(2 << 12);
  26. BufferedReader reader = null;
  27. try {
  28. reader = request.getReader();
  29. while (-1 != reader.read(buffer)) {
  30. builder.append(buffer.flip());
  31. }
  32. } catch (Exception e) {
  33. e.printStackTrace();
  34. } finally {
  35. try {
  36. reader.close();
  37. } catch (IOException e) {
  38. e.printStackTrace();
  39. }
  40. }
  41. String bodyStr = builder.toString();
  42. try {
  43. bodyStr = URLDecoder.decode(bodyStr, "UTF-8");
  44. } catch (UnsupportedEncodingException e) {
  45. e.printStackTrace();
  46. throw new RuntimeException("body解码出错!");
  47. }
  48. body = bodyStr.getBytes(Charset.defaultCharset());
  49. }
  50. public byte[] getBody() {
  51. return body;
  52. }
  53. @Override
  54. public BufferedReader getReader() throws IOException {
  55. return new BufferedReader(new InputStreamReader(getInputStream()));
  56. }
  57. @Override
  58. public ServletInputStream getInputStream() throws IOException {
  59. final ByteArrayInputStream bais = new ByteArrayInputStream(body);
  60. return new ServletInputStream() {
  61. @Override
  62. public boolean isFinished() {
  63. return bais.available() == 0;
  64. }
  65. @Override
  66. public boolean isReady() {
  67. return true;
  68. }
  69. @Override
  70. public void setReadListener(ReadListener readListener) {
  71. }
  72. @Override
  73. public int read() throws IOException {
  74. return bais.read();
  75. }
  76. };
  77. }
  78. }

总结
1.了解过滤器和拦截器的区别
2.过滤器推荐使用第2种方法,使用spring管理,可以设置order
3.过滤器中如果读取了body数据,需要回写回去,让后续操作能读取到body

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/921902?site
推荐阅读
相关标签
  

闽ICP备14008679号