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

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 {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {System.out.println("==过滤逻辑==");chain.doFilter(request, response);}}

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

2.2 使用FilterRegistrationBean对象,将过滤器添加到spring容器,需配合@Configuration和@Bean注解
        a.写过滤器类,实现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()方法中继续传下去,后续就能继续读取了*/
@Component
public class RequestBodyFilter implements Filter {@Overridepublic 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);}}}

        b.将过滤器添加到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;@Configuration
public class MyFilterConfig {@Autowiredprivate RequestBodyFilter requestBodyFilter;/*** 将过滤器注册到FilterRegistrationBean中* 进行简单配置*/@Beanpublic 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;}@Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new InputStreamReader(getInputStream()));}@Overridepublic ServletInputStream getInputStream() throws IOException {final ByteArrayInputStream bais = new ByteArrayInputStream(body);return new ServletInputStream() {@Overridepublic boolean isFinished() {return bais.available() == 0;}@Overridepublic boolean isReady() {return true;}@Overridepublic void setReadListener(ReadListener readListener) {}@Overridepublic int read() throws IOException {return bais.read();}};}
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/484507.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何自定义一个协议

. 如何自定义一个协议 先有一个需求,有个场景,打开外卖软件,会显示商家列表,列表中有很多项,每一项都包含了一些信息,商家的名称,图片,好评率,距离你的位置,评分 这些信息都是通过网络,从服务器获取的, 客户端,需要给服务器发送一个请求,服务器收到请求之后,就给客户端返回一个…

HTTP攻击,该怎么防护

一般网络世界里为人们所熟知的DDoS攻击&#xff0c;多数是通过对带宽或网络计算资源的持续、大量消耗&#xff0c;最终导致目标网络与业务的瘫痪&#xff1b;这类DDOS攻击&#xff0c;工作在OSI模型的网络层与传输层&#xff0c;利用协议特点构造恶意的请求载荷来达成目标资源耗…

从底层理解MySQL-字符类型

目录 VARCHAR和CHAR VARCHAR CHAR 存储的长度超限 CHAR和VARCHAR的区别&#xff1a; BLOB和TEXT MySQL中除了数值类型外&#xff0c;另一个用的比较多的就是字符类型了。字符类型有很多不同种类&#xff1a;VARCHAR,CHAR,BLOB,TEXT VARCHAR和CHAR VARCHAR VARCHAR是变…

如何使用ArcGIS Pro生成等高线

无论在制图还是规划中&#xff0c;经常会使用到等高线&#xff0c;大多数情况下&#xff0c;从网上获取的高程数据都是DEM文件&#xff0c;我们可以通过ArcGIS Pro来生成等高线&#xff0c;这里为大家介绍一下生成方法&#xff0c;希望能对你有所帮助。 数据来源 教程所使用的…

springboot206基于SpringBoot的农商对接系统的设计与实现

基于Spring Boot的农商对接系统的设计与实现 Design and implementation of agricultural business docking system based on Spring Boot 摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离…

【Chrono Engine学习总结】4-vehicle-4.3-两个vehicle碰撞测试

由于Chrono的官方教程在一些细节方面解释的并不清楚&#xff0c;自己做了一些尝试&#xff0c;做学习总结。 今天突发奇想&#xff0c;想试一下&#xff0c;是否可以实现两个vehicle的碰撞&#xff1f; 1、两辆vehicle的仿真 官方提供了demo_VEH_TwoCars这个demo&#xff0c…

React -- useEffect

React - useEffect 概念理解 useEffect是一个React Hook函数&#xff0c;用于在React组件中创建不是由事件引起而是由渲染本身引起的操作&#xff08;副作用&#xff09;, 比 如发送AJAX请求&#xff0c;更改DOM等等 :::warning 说明&#xff1a;上面的组件中没有发生任何的用…

暴风一期 黑群晖折腾流水账

起因是想和女友一起分享各自手机中的小猫照片&#xff0c;经过上网查资料了解到了群晖&#xff0c;可惜正版白群晖售价太高&#xff1a; 无奈选择了黑群晖&#xff0c;白群晖其实也可以简单理解为一台电脑多装了几块硬盘&#xff0c;然后装了一个文件服务器系统&#xff0c;这个…

CoordConv(NeurIPS 2018)

paper&#xff1a;An Intriguing Failing of Convolutional Neural Networks and the CoordConv Solution official implementation&#xff1a;https://github.com/uber-research/coordconv 存在的问题 本文揭示并分析了CNN在两种不同类型空间表示之间转换能力的欠缺&#…

每日OJ题_二叉树dfs⑤_力扣230. 二叉搜索树中第K小的元素

目录 力扣230. 二叉搜索树中第K小的元素 解析代码 力扣230. 二叉搜索树中第K小的元素 230. 二叉搜索树中第K小的元素 难度 中等 给定一个二叉搜索树的根节点 root &#xff0c;和一个整数 k &#xff0c;请你设计一个算法查找其中第 k 个最小元素&#xff08;从 1 开始计数…

RocketMQ—RocketMQ消息重复消费问题

RocketMQ—RocketMQ消息重复消费问题 重复消费问题的描述 什么情况下会发生重复消费的问题&#xff1a; 生产者多次投递消息&#xff1a;如果生产者发送消息时&#xff0c;连接有延迟&#xff0c;MQ还没收到消息&#xff0c;生产者又发送了一次消息&#xff1b; 消费者方扩容…

3个wordpress中文企业主题模板

农业畜牧养殖wordpress主题 简洁大气的农业畜牧养殖wordpress主题&#xff0c;农业农村现代化&#xff0c;离不开新农人、新技术。 https://www.jianzhanpress.com/?p3051 老年公寓wordpress主题 浅绿色简洁实用的老年公寓wordpress主题&#xff0c;适合做养老业务的老年公…