当前位置:   article > 正文

SpringBoot之拦截器与过滤器解读

SpringBoot之拦截器与过滤器解读

目录

一、SpringBoot 拦截器 过滤器

二、实现HandleInterceptor接口

三、继承HandleInterceptorAdapter类

四、实现RequestInterceptor接口 

 五、粉丝福利


一、SpringBoot 拦截器 过滤器

1、过滤器和拦截器触发时机不一样,过滤器是在请求进入容器后,但请求进入servlet之前进行预处理的。请求结束返回也是,是在servlet处理完后,返回给前端之前。

2、拦截器可以获取IOC容器中的各个bean,而过滤器就不行,因为拦截器是spring提供并管理的,spring的功能可以被拦截器使用,在拦截器里注入一个service,可以调用业务逻辑。而过滤器是JavaEE标准,只需依赖servlet api ,不需要依赖spring。

3、过滤器的实现基于回调函数。而拦截器(代理模式)的实现基于反射

4、Filter是依赖于Servlet容器,属于Servlet规范的一部分,而拦截器则是独立存在的,可以在任何情况下使用。

5、Filter的执行由Servlet容器回调完成,而拦截器通常通过动态代理(反射)的方式来执行。

6、Filter的生命周期由Servlet容器管理,而拦截器则可以通过IoC容器来管理,因此可以通过注入等方式来获取其他Bean的实例,因此使用会更方便。

过滤器和拦截器非常相似,但是它们有很大的区别最简单明了的区别就是**过滤器可以修改request,而拦截器不能过滤器需要在servlet容器中实现,拦截器可以适用于javaEE,javaSE等各种环境拦截器可以调用IOC容器中的各种依赖,而过滤器不能过滤器只能在请求的前后使用,而拦截器可以详细到每个方法**区别很多,大家可以去查下

总的来说过滤器就是筛选出你要的东西,比如requeset中你要的那部分拦截器在做安全方面用的比较多,比如 权限验证

下面是拦截器的例子:

拦截器定义:

二、实现HandleInterceptor接口

自定义拦截器类实现HandleInterceptor接口,并使用@Component注解标注为一个组件。

@Component注解 是为了 注入spring其他组件方便, 如果没有这个注解,自动注入为空

@Autowired   

UserService userService;

  1. public class MySelfInterceptor implements HandlerInterceptor {
  2. @Override
  3. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  4. System.out.println("在业务处理器处理请求之前被调用");
  5. //可以进行权限校验,安全控制
  6. MyRequestWrapper requestWrapper = new MyRequestWrapper (request);
  7. // 读取请求内容
  8. BufferedReader br = requestWrapper.getReader();
  9. String line = null;
  10. StringBuilder sb = new StringBuilder();
  11. while ((line = br.readLine()) != null) {
  12. sb.append(line);
  13. }
  14. // 将json字符串转换为json对象
  15. JSONObject body = JSONObject.parseObject(sb.toString());
  16. //业务处理
  17. return true;
  18. }
  19. @Override
  20. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  21. System.out.println("在业务处理器处理请求执行完成后,生成视图之前执行");
  22. //可以对返回来的ModelAndView进行处理,这个时候还未渲染视图
  23. }
  24. @Override
  25. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  26. System.out.println("在DispatcherServlet完全处理完请求后被调用");
  27. //请求已经完成,页面已经渲染,数据已经返回。这个时候可以做一些资源清理,或者记录请求调用时间,做性能监控
  28. }
  29. }

三、继承HandleInterceptorAdapter类

自定义拦截器类继承HandleInterceptor接口的实现类HandleInterceptorAdapter来定义,并使用@Component注解标注为一个组件。

可以根据需要覆盖一些方法

  1. @Component
  2. public class MyInterceptor extends HandlerInterceptorAdapter {
  3. public SingleLoginInterceptor() {
  4. super();
  5. }
  6. @Override
  7. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  8. return super.preHandle(request, response, handler);
  9. }
  10. @Override
  11. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  12. super.postHandle(request, response, handler, modelAndView);
  13. }
  14. @Override
  15. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  16. super.afterCompletion(request, response, handler, ex);
  17. }
  18. @Override
  19. public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  20. super.afterConcurrentHandlingStarted(request, response, handler);
  21. }
  22. }

可以看到HandlerInterceptorAdapter类底层是实现了HandlerInterceptor接口,多了两个方法,要比实现HandlerInterceptor接口的方式功能强大。

这两个方法都是HandlerInterceptorAdapter类实现的org.springframework.web.servlet.AsyncHandlerInterceptor接口提供的,而AsyncHandlerInterceptor接口又继承了HandlerInterceptor接口,所以HandlerInterceptorAdapter底层是实现类HandlerInterceptor接口。

自定义拦截器类实现WebRequestInterceptor接口,并使用@Component注解标注为一个组件。

  1. @Component
  2. public class MyInterceptor implements WebRequestInterceptor {
  3. @Override
  4. public void preHandle(WebRequest webRequest) throws Exception {
  5. }
  6. @Override
  7. public void postHandle(WebRequest webRequest, ModelMap modelMap) throws Exception {
  8. }
  9. @Override
  10. public void afterCompletion(WebRequest webRequest, Exception e) throws Exception {
  11. }
  12. }

两个实现接口方式的异同点 相同点 都可以实现controller层的拦截请求 不同点

  • 1.WebRequestInterceptor的入参WebRequest是包装了HttpServletRequest 和HttpServletResponse的,通过WebRequest获取Request中的信息更简便。
  • 2.WebRequestInterceptor的preHandle是没有返回值的,说明该方法中的逻辑并不影响后续的方法执行,所以这个接口实现就是为了获取Request中的信息,或者预设一些参数供后续流程使用。
  • 3.HandlerInterceptor的功能更强大也更基础,可以在preHandle方法中就直接拒绝请求进入controller方法。

四、实现RequestInterceptor接口 

此方式为微服务Feign调用的自定义拦截器,实现各个微服务之间的参数传递。

  1. @Configuration
  2. public class CenterinsRequestInterceptor implements RequestInterceptor {
  3. @Override
  4. public void apply(RequestTemplate requestTemplate) {
  5. }
  6. }

拦截器的注册

创建一个自定义类,继承WebMvcConfigurerAdapter类重写addInterceptors方法。

  1. @Configuration
  2. public class WebCofiguration extends WebMvcConfigurerAdapter {
  3. @Bean
  4. MyInterceptor getMyInterceptor (){
  5. return new MyInterceptor ();
  6. }
  7. public void addInterceptors(InterceptorRegistry registry) {
  8. // 将自己定义的拦截器注入进来进行拦截操作
  9. //registry.addInterceptor(new MySelfInterceptor ()) // 如果是new 出来的对象 会导致 拦截器中自动装配为空
  10. registry.addInterceptor(getMyInterceptor ())
  11. .addPathPatterns("/**")
  12. .excludePathPatterns("/logout");
  13. //过滤器可以添加多个,这里的addPathPatterns的/**是对所有的请求都做拦截。
  14. //excludePathPatterns代表排除url的拦截路径,即不拦截
  15. }
  16. }

此类在SpringBoot2.0以后已经废除,但仍可使用。

推荐使用以下两种方式来代替此方式。

1. 创建一个自定义类继承WebMvcConfigurationSupport类,实现addInterceptors。

  1. @Configuration
  2. public class MyInterceptorConfig extends WebMvcConfigurationSupport {
  3. @Override
  4. protected void addInterceptors(InterceptorRegistry registry) {
  5. registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
  6. super.addInterceptors(registry);
  7. }
  8. }

此方式会导致默认的静态资源被拦截,这就需要我们手动将静态资源放开。

除了重写方法外还需要重写addResourceHandlers方法来释放静态资源

  1. @Override
  2. protected void addResourceHandlers(ResourceHandlerRegistry registry) {
  3. registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
  4. super.addResourceHandlers(registry);
  5. }

此方式:一个容器内只能有一个WebMvcConfigurationSupport的实现类,也就是说不能有多个继承类,否则只有一个生效,会造成未知的错误,如果想在已有实现类的基础上(基础jar包中存在webConfig)还想继续添加拦截器,可以选择继承WebConfig,但是要super.addInterceptors,避免丢失注册

原因:在WebMvcAutoConfiguration 中 WebMvcConfigurationSupport 是  @ConditionalOnMissingBean 原来SpringBoot做了这个限制,只有当WebMvcConfigurationSupport类不存在的时候才会生效WebMvc自动化配置

2. 实现WebMvcConfigurer接口

  1. @Configuration
  2. public class MyInterceptorConfig implements WebMvcConfigurer {
  3. @Override
  4. public void addInterceptors(InterceptorRegistry registry) {
  5. // 实现WebMvcConfigurer不会导致静态资源被拦截
  6. registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
  7. }
  8. }

 另外可以写个配置注解,根据注解拦截需要的方法

  1. import java.lang.annotation.ElementType;
  2. import java.lang.annotation.Retention;
  3. import java.lang.annotation.RetentionPolicy;
  4. import java.lang.annotation.Target;
  5. @Target(ElementType.METHOD)
  6. @Retention(RetentionPolicy.RUNTIME)
  7. public @interface LogData {
  8. }

在controller中需要拦截的方法上加上 @LogData

  1. @LogData
  2. public ResponseMessage getUserList(@RequestParam Long id) {
  3. return ResponseMessage.ok();
  4. }

可以在拦截器中

  1. @Override
  2. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  3. //这个方法将在请求处理之前进行调用。注意:如果该方法的返回值为false ,将视为当前请求结束,不仅自身的拦截器会失效,还会导致其他的拦截器也不再执行。
  4. log.info("进入到拦截器中:preHandle() 方法");
  5. HandlerMethod handlerMethod = (HandlerMethod) handler;
  6. LogData loginVerify = handlerMethod.getMethodAnnotation(LogData .class);
  7. if (loginVerify == null) {
  8. log.info("不需要对该路径 进行拦截");
  9. return true;
  10. }else {
  11. log.info("对该路径 进行拦截");
  12. log.info("业务操作...");
  13. return true;
  14. }
  15. }

拦截器中request请求被读取一次后,controller获取为空

继承HandleInterceptorAdapter类  和 实现HandleInterceptor接口  实现WebRequestInterceptor接口  自定义类实现RequestInterceptor接口

HttpServletRequest的输入流只能读取一次的原因当我们调用getInputStream()方法获取输入流时得到的是一个InputStream对象,而实际类型是ServletInputStream,它继承与InputStream。

InputStream的read()方法内部有一个position,标志当前流被读取到的位置,每读取一次,该标志就会移动一次,如果读到最后,read()返回-1,表示已经读取完了,如果想要重新读取,则需要调用reset()方法,position就会移动到上次调用mark的位置,mark默认是0,所有就能重头再读了。调用reset()方法的前提是已经重写了reset()方法,当然能否reset也是有条件的,它取决于markSupported()方法是否返回true。

InputStream默认不实现reset(),并且markSupported()默认也是返回false

我们可以把流读取出来后用容器存起来,后面就可以多次利用了。JavaEE提供了一个HttpServletRequestWrapper类,它是一个http请求包装器,基于装饰者模式实现类HttpServletRequest界面。

继承HttpServletRequestWrapper,将请求体中的流copy一份,可以重写getinputStream()和getReader()方法,或自定义方法供外部使用

  1. import dm.jdbc.e.e;
  2. import dm.jdbc.util.StreamUtil;
  3. import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
  4. import lombok.extern.slf4j.Slf4j;
  5. import javax.servlet.ReadListener;
  6. import javax.servlet.ServletInputStream;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletRequestWrapper;
  9. import java.io.*;
  10. import java.nio.charset.StandardCharsets;
  11. /**
  12. * 重写 HttpServletRequestWrapper
  13. *
  14. */
  15. @Slf4j
  16. public class MyRequestWrapper extends HttpServletRequestWrapper {
  17. private byte[] body; //用于保存读取body中数据
  18. public MyRequestWrapper (HttpServletRequest request) throws IOException {
  19. super(request);
  20. //读取请求的数据保存到本类当中
  21. //body = StreamUtil.readBytes(request.getReader(), "UTF-8");
  22. StringBuilder stringBuilder = new StringBuilder();
  23. BufferedReader bufferedReader = null;
  24. InputStream inputStream = null;
  25. try{
  26. inputStream = request.getInputStream();
  27. if(inputStream != null){
  28. bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
  29. char[] charBuffer = new char[128];
  30. int bytesRead =-1;
  31. while((bytesRead = bufferedReader.read(charBuffer)) >0){
  32. stringBuilder.append(charBuffer,0, bytesRead);
  33. }
  34. }else{
  35. stringBuilder.append("");
  36. }
  37. }catch (Exception e){
  38. }finally {
  39. if(inputStream != null){
  40. inputStream.close();
  41. }
  42. if(bufferedReader != null){
  43. bufferedReader.close();
  44. }
  45. }
  46. body = stringBuilder.toString().getBytes();
  47. }
  48. //覆盖(重写)父类的方法
  49. @SuppressFBWarnings("DM_DEFAULT_ENCODING")
  50. @Override
  51. public BufferedReader getReader() throws IOException {
  52. return new BufferedReader(new InputStreamReader(getInputStream()));
  53. }
  54. //覆盖(重写)父类的方法
  55. @Override
  56. public ServletInputStream getInputStream() throws IOException {
  57. final ByteArrayInputStream bais = new ByteArrayInputStream(body);
  58. return new ServletInputStream() {
  59. @Override
  60. public int read() throws IOException {
  61. return bais.read();
  62. }
  63. @Override
  64. public boolean isFinished() {
  65. // TODO Auto-generated method stub
  66. return false;
  67. }
  68. @Override
  69. public boolean isReady() {
  70. // TODO Auto-generated method stub
  71. return false;
  72. }
  73. @Override
  74. public void setReadListener(ReadListener arg0) {
  75. // TODO Auto-generated method stub
  76. }
  77. };
  78. }
  79. /**
  80. * 获取body中的数据
  81. * @return
  82. */
  83. public byte[] getBody() {
  84. return body;
  85. }
  86. /**
  87. * 把处理后的参数放到body里面
  88. * @param body
  89. */
  90. public void setBody(byte[] body) {
  91. this.body = body;
  92. }
  93. }

定义过滤器

  1. import lombok.extern.slf4j.Slf4j;
  2. import javax.servlet.*;
  3. import javax.servlet.annotation.WebFilter;
  4. import javax.servlet.http.HttpServletRequest;
  5. import java.io.IOException;
  6. /**
  7. * 过滤器
  8. *
  9. */
  10. @Slf4j
  11. @WebFilter(urlPatterns = "/*", filterName = "logSignDataFilter")
  12. public class LogSignDataFilter implements Filter {
  13. @Override
  14. public void destroy() {
  15. }
  16. @Override
  17. public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
  18. ServletRequest requestWrapper = null;
  19. if(request instanceof HttpServletRequest){
  20. requestWrapper = new MyRequestWrapper ((HttpServletRequest) request);
  21. }
  22. if(requestWrapper == null){
  23. filterChain.doFilter(request, response);
  24. }else{
  25. filterChain.doFilter(requestWrapper, response);
  26. }
  27. }
  28. @Override
  29. public void init(FilterConfig config) throws ServletException {
  30. }
  31. }

 五、粉丝福利

最近很多同学问我有没有java学习资料,我根据我从小白到架构师多年的学习经验整理出来了一份50W字面试解析文档、简历模板、学习路线图、java必看学习书籍 、 需要的小伙伴 可以关注我
公众号:“ 灰灰聊架构 ”, 回复暗号:“ 321 ”即可获取

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

闽ICP备14008679号