SpringBoot添加过滤器Filter

手机软件开发 2024-10-3 08:14:08 77 0 来自 中国
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.jpeg 1.2.4 处置处罚范围差异

过滤器处置处罚逻辑都在doFilter方法中,拦截器区分pre、post和after,分别粒度更细了,使用起来更机动

2.png

1.2.5 拦截器可以注入业务

拦截器可以获取IOC容器中的各个bean,而过滤器就不可,这点很紧张,在拦截器里注入一个service,可以调用业务逻辑。
2. Springboot添加拦截器

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



    • 写过滤器类,并实现Filter接口


    • 添加@WebFilter注解,属性filterName设置过滤器名称,urlPatterns匹配要过滤的url

import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.annotation.WebFilter;@WebFilter(filterName = "testFilter", urlPatterns = "/*")public class TestFilter implements Filter {    @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)            throws IOException, ServletException {                System.out.println("==过滤逻辑==");        chain.doFilter(request, response);            }}


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

2.2 使用FilterRegistrationBean对象,将过滤器添加到spring容器,需共同@Configuration和@Bean注解



    • 写过滤器类,实现Filter接口,并添加 @Component注解

import java.io.IOException;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import org.apache.http.entity.ContentType;import org.springframework.http.HttpMethod;import org.springframework.stereotype.Component;/** * gateway转发参数时,有特别符号如 * +等颠末了urlencode,无法精确吸收, * 加过滤器将 body进行urldecode *  * 留意的标题:RequestBody注解的数据, * 要通过request的getReader和getInputStream()方法来获取流数据,然后变化为字符串的 * 但是流读取一次后就关闭了,在filter中读取了以后,controller中就拿不到数据了 * 为此,我们自界说一个HttpServletRequestWrapper对象,将拿到的数据在放到这个对象中, * 并将这个对象从chain.doFilter()方法中继续传下去,后续就能继续读取了 */@Componentpublic class RequestBodyFilter implements Filter {    @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)            throws IOException, ServletException {        HttpServletRequest req = (HttpServletRequest) request;        String contextPath = request.getServletContext().getContextPath();        boolean b1 = HttpMethod.POST.name().equals(req.getMethod());        boolean b2 = ContentType.APPLICATION_JSON.getMimeType().equals(request.getContentType());        boolean b3 = request.getParameterMap().isEmpty();        boolean b4 = contextPath.contains("/test");        if (b1 && b2 && b3 && b4) {            MyHttpServletRequestWrapper requestWrapper = new MyHttpServletRequestWrapper(req);            chain.doFilter(requestWrapper, response);        } else {            chain.doFilter(request, response);        }            }}


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

import javax.servlet.Filter;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class MyFilterConfig {        @Autowired    private RequestBodyFilter requestBodyFilter;        /**     * 将过滤器注册到FilterRegistrationBean中     * 进行简单配置     */    @Bean    public FilterRegistrationBean<Filter> someFilterRegistration() {        FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();        registration.setFilter(requestBodyFilter);        registration.addUrlPatterns("/*");        registration.setName("requestBodyFilter");        registration.setOrder(99);        return registration;    }}


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

import java.io.BufferedReader;import java.io.ByteArrayInputStream;import java.io.IOException;import java.io.InputStreamReader;import java.io.UnsupportedEncodingException;import java.net.URLDecoder;import java.nio.CharBuffer;import java.nio.charset.Charset;import javax.servlet.ReadListener;import javax.servlet.ServletInputStream;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;/** * 自界说request对象,将body处置处罚后,放到这个对象内里, * 一定要重写getReader()和getInputStream()方法,将body放进去, * 如许反面的controller才气从这个request的getReader()和getInputStream() * 方法中拿到新的body对象 */public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {        private byte[] body;        public MyHttpServletRequestWrapper(HttpServletRequest request) {        super(request);                final StringBuilder builder = new StringBuilder();        // 缓存按 8192 巨细        final CharBuffer buffer = CharBuffer.allocate(2 << 12);        BufferedReader reader = null;        try {            reader = request.getReader();            while (-1 != reader.read(buffer)) {                builder.append(buffer.flip());            }        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                reader.close();            } catch (IOException e) {                e.printStackTrace();            }        }                String bodyStr = builder.toString();        try {            bodyStr = URLDecoder.decode(bodyStr, "UTF-8");        } catch (UnsupportedEncodingException e) {            e.printStackTrace();            throw new RuntimeException("body解码堕落!");        }        body = bodyStr.getBytes(Charset.defaultCharset());    }        public byte[] getBody() {        return body;    }    @Override    public BufferedReader getReader() throws IOException {        return new BufferedReader(new InputStreamReader(getInputStream()));    }            @Override    public ServletInputStream getInputStream() throws IOException {        final ByteArrayInputStream bais = new ByteArrayInputStream(body);        return new ServletInputStream() {            @Override            public boolean isFinished() {                return bais.available() == 0;            }            @Override            public boolean isReady() {                return true;            }            @Override            public void setReadListener(ReadListener readListener) {            }            @Override            public int read() throws IOException {                return bais.read();            }        };    }}总结


  • 相识过滤器和拦截器的区别
  • 过滤器保举使用第2种方法,使用spring管理,可以设置order
  • 过滤器中假如读取了body数据,必要回写归去,让后续使用能读取到body
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 16:42, Processed in 0.152260 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表