SpringBoot 过滤器、拦截器、监听器对比及使用场景分析

一、关系图理解

二、区别

1.过滤器

  • 过滤器是在web应用启动的时候初始化一次, 在web应用停止的时候销毁
  • 可以对请求的URL进行过滤, 对敏感词过滤
  • 挡在拦截器的外层
  • 实现的是 javax.servlet.Filter 接口 ,是 Servlet 规范的一部分
  • 在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后
  • 依赖Web容器
  • 会多次执行

过滤器简介

过滤器的英文名称为 Filter, 是 Servlet 技术中最实用的技术。如同它的名字一样,过滤器是处于客户端和服务器资源文件之间的一道过滤网,帮助我们过滤掉一些不符合要求的请求,通常用作 Session 校验,判断用户权限,如果不符合设定条件,则会被拦截到特殊的地址或者基于特殊的响应。

过滤器的使用

首先需要实现 Filter接口然后重写它的三个方法
•init 方法:在容器中创建当前过滤器的时候自动调用
•destory 方法:在容器中销毁当前过滤器的时候自动调用
•doFilter 方法:过滤的具体操作

1.1HttpServletRequestWrapper

在请求到达之前对 request 进行修改

package com.dingwen.lir.filter;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.Arrays;

/**
 *  在请求到达之前对 request 进行修改
 *
 * @author dingwen
 * 2021.04.30 14:54
 */
@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {
    public RequestWrapper(HttpServletRequest request) {
        super(request);
        log.info("RequestWrapper");
    }

    @Override
    public String getParameter(String name) {
        // 可以对请求参数进行过滤
        return super.getParameter(name);
    }

    @Override
    public String[] getParameterValues(String name) {
        // 对请求参数值进行过滤
//        String[] values =super.getRequest().getParameterValues(name);
//        return super.getParameterValues(name);
        return "t e s t".split(" ");
    }

}

1.2 OncePerRequestFilter

OncePerRequestFilter,顾名思义,它能够确保在一次请求中只通过一次filter

package com.dingwen.lir.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;

/**
 * 请求过滤器
 * OncePerRequestFilter:
 * OncePerRequestFilter,顾名思义,它能够确保在一次请求中只通过一次filter.
 * 大家常识上都认为,一次请求本来就只filter一次,为什么还要由此特别限定呢,往往我们的常识和实际的实现并不真的一样,经过一番资料的查阅,此方法是为了兼容不同的web container,
 * 也就是说并不是所有的container都入我们期望的只过滤一次,servlet版本不同,执行过程也不同,
 * 因此,为了兼容各种不同运行环境和版本,默认filter继承OncePerRequestFilter是一个比较稳妥的选择。
 *
 * @author dingwen
 * 2021.04.30 15:59
 */
@Slf4j
public class RequestFilter extends OncePerRequestFilter {

    @Override
    public void destroy() {
        super.destroy();
        log.info("RequestFilter destroy");
    }

    /*
            OncePerRequestFilter.doFilter方法中通过request.getAttribute判断当前过滤器是否已执行
            若未执行过,则调用doFilterInternal方法,交由其子类实现
        */
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        try {
            RequestWrapper requestWrapper = new RequestWrapper(httpServletRequest);
            filterChain.doFilter(requestWrapper, httpServletResponse);
            log.info("RequestFilter");
            log.info(Arrays.toString(requestWrapper.getParameterValues("name")));
        } catch (Exception exception) {
            httpServletResponse.setCharacterEncoding("utf-8");
            httpServletResponse.setContentType("application/json; charset=utf-8");
            PrintWriter writer = httpServletResponse.getWriter();
            writer.write(exception.toString());
        }
    }
}

1.3 配置

package com.dingwen.lir.configuration;

import com.dingwen.lir.filter.RequestFilter;
import com.dingwen.lir.filter.RequestWrapper;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;

/**
 * 过滤器配置类
 *
 * @author dingwen
 * 2021.04.30 16:10
 */
@Configuration
public class FilterConfig {

    @Bean
    public RequestFilter requestFilter(){
        return new RequestFilter();
    }
    @Bean
    public FilterRegistrationBean<RequestFilter> registrationBean() {
        FilterRegistrationBean<RequestFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(requestFilter());
        registrationBean.addUrlPatterns("/filter/*");
        registrationBean.setName("RequestFilter");
        //过滤器的级别,值越小级别越高越先执行
        registrationBean.setOrder(1);
        return registrationBean;
    }
}

2.拦截器

  • 实现 org.springframework.web.servlet.HandlerInterceptor 接口,动态代理
  • 拦截器应用场景, 性能分析, 权限检查, 日志记录
  • 是一个Spring组件,并由Spring容器管理,并不
  • 依赖Tomcat等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于Application、Swing等程序中
  • 是在请求进入servlet后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束

2.1登录拦截

package com.dingwen.lir.interceptor;

import com.dingwen.lir.entity.User;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 登录拦截
 *
 * @author dingwen
 * 2021.04.25 13:50
 */
@Component
public class PageInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        User user = (User)request.getSession().getAttribute("user");
        if (!ObjectUtils.isEmpty(user)) {
            return true;
        } else {
            // 不管是转发还是重定向,必须返回false。否则出现多次提交响应的错误
            redirect(request, response);
            return false;
        }
    }

    /*
     * 对于请求是ajax请求重定向问题的处理方法
     * @param request
     * @param response
     *
     */
    public void redirect(HttpServletRequest request, HttpServletResponse response) throws IOException {

        if("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))){// ajax
            //获取当前请求的路径
            response.setHeader("Access-Control-Expose-Headers", "REDIRECT,CONTENT_PATH");
            //告诉ajax我是重定向
            response.setHeader("REDIRECT", "REDIRECT");
            //告诉ajax我重定向的路径
            StringBuffer url = request.getRequestURL();
            String contextPath = request.getContextPath();
            response.setHeader("CONTENT_PATH", url.replace(url.indexOf(contextPath) + contextPath.length(), url.length(), "/").toString());
        }else{// http
            response.sendRedirect( "/page/login");
        }

        response.getWriter().write(403);
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
    }
}

2.2配置

package com.dingwen.lir.configuration;

import com.dingwen.lir.interceptor.PageInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * mvc 控制器配置
 * MyWebMvcConfigurer: Springboot2.x以后版本使用
 *
 * @author dingwen
 * 2021.04.26 17:52
 */
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {

    /*
     * 拦截器依赖于Spring容器,此处拦截了所有,需要对静态资源进行放行
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 拦截器默认的执行顺序,就是它的注册顺序,也可以通过Order手动设置控制,值越小越先执行。
//        registry.addInterceptor(new PageInterceptor()).addPathPatterns("/**").order()
        registry.addInterceptor(new PageInterceptor()).addPathPatterns("/**")
                .excludePathPatterns("/page/login", "/user/login","/page/ajax","/static/**");
    }

    /*
     * 不要要写控制器即可完成页面跳转访问
     * @param registry
     */
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/page/ajax").setViewName("ajax");
    }

    /*
     * 自定义静态资源映射
        Spring Boot 默认为我们提供了静态资源映射:
                classpath:/META-INF/resources
                classpath:/resources
                classpath:/static
                classpath:/public
              优先级:META-INF/resources > resources > static > public
     * @param registry
     *
     */
//    @Override
//    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
        registry.addResourceHandler("/static/**").addResourceLocations("file:E:/static/");
//    }
}

3.监听器

  • 实现 javax.servlet.ServletRequestListener, javax.servlet.http.HttpSessionListener, javax.servlet.ServletContextListener 等等接口
  • 主要用来监听对象的创建与销毁的发生, 比如 session 的创建销毁, request 的创建销毁, ServletContext 创建销毁

三、注意

1.静态资源问题

SpringBoot2.x以后版本拦截器也会拦截静态资源,在配置拦截器是需要将姿态资源放行。

 /*
     * 拦截器依赖于Spring容器,此处拦截了所有,需要对静态资源进行放行
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new PageInterceptor()).addPathPatterns("/**")
                .excludePathPatterns("/page/login", "/user/login","/page/ajax","/static/**");
    }

SpringBoot2.x 自定义静态资源映射

spring:
  mvc:
    static-path-pattern: /static/**

默认目录
classpath:/META-INF/resources
classpath:/resources
classpath:/static
classpath:/public
优先级:META-INF/resources > resources > static > public

2.登录拦截ajax重定向

由于ajax是异步的,还在当前页面进行的局部请求。当拦截到登录请求时,即使重定向也无法生效。需采用服务端给地址由前端进行跳转。详细见登录拦截器代码。

// 前端处理
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>AJAX</title>
    <script src="https://code.jquery.com/jquery-3.0.0.min.js"></script>
</head>
<body>
    <button>USER</button>
</body>
</html>

<script>
    $.ajaxSetup({
        complete:function(xhr,status){
            //拦截器实现超时跳转到登录页面
            let win = window;
            // 通过xhr取得响应头
            let REDIRECT = xhr.getResponseHeader("REDIRECT");
            //如果响应头中包含 REDIRECT 则说明是拦截器返回的需要重定向的请求
            if (REDIRECT === "REDIRECT")
            {
                while (win !== win.top)
                {
                    win = win.top;
                }
                win.location.href = xhr.getResponseHeader("CONTEXTPATH");
            }
        }
    });
    $("button").click(function(){
        $.get("/page/user", function(result){
            $("div").html(result);
        });
    });
</script>

四、测试

代码地址:https://gitee.com/dingwen-gitee/filter-interceptor-study.git

1.拦截器测试

1.1启动项目访问首页

http://localhost:8080/page/index

由于没有登录,直接重定向到了登录页

1.2输入用户名密码完成登录,调转到用户页


此时在访问首页

1.2 退出登录

成功退出后,访问为授权的页面也相对会被重定向到登录页

1.3 ajax未授权访问测试

点击访问user ,由于未登录,没有全权访问。在前端进行了页面跳转,转到了登录页。

2.过滤器测试


可以看到过滤器进行了相对应的处理,重写的getParameterValues()也生效了。配合使用HttpServletRequestWrapper & OncePerRequestFilter 实现了对request的修改。

到此这篇关于SpringBoot 过滤器、拦截器、监听器对比及使用场景分析的文章就介绍到这了,更多相关SpringBoot 过滤器、拦截器、监听器内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot拦截器实现登录拦截的方法示例

    源码 GitHub:https://github.com/291685399/springboot-learning/tree/master/springboot-interceptor01 SpringBoot拦截器可以做什么 可以对URL路径进行拦截,可以用于权限验证.解决乱码.操作日志记录.性能监控.异常处理等 SpringBoot拦截器实现登录拦截 pom.xml: <?xml version="1.0" encoding="UTF-8"?> &

  • SpringBoot实现拦截器、过滤器、监听器过程解析

    这篇文章主要介绍了SpringBoot实现拦截器.过滤器.监听器过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 过滤器 过滤器简介 过滤器的英文名称为 Filter, 是 Servlet 技术中最实用的技术.如同它的名字一样,过滤器是处于客户端和服务器资源文件之间的一道过滤网,帮助我们过滤掉一些不符合要求的请求,通常用作 Session 校验,判断用户权限,如果不符合设定条件,则会被拦截到特殊的地址或者基于特殊的响应. 过滤器的使用 首

  • SpringBoot定义过滤器、监听器、拦截器的方法

    一.自定义过滤器 创建一个过滤器,实现javax.servlet.Filter接口,并重写其中的init.doFilter.destory方法. package com.example.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.Se

  • 详谈springboot过滤器和拦截器的实现及区别

    前言 springmvc中有两种很普遍的AOP实现: 1.过滤器(Filter) 2.拦截器(Interceptor) 本篇面对的是一些刚接触springboot的人群 所以主要讲解filter和interceptor的简单实现和它们之间到底有什么区别 (一些复杂的功能我会之后发出文章,请记得关注) Filter的简单实现 字面意思:过滤器就是过滤的作用,在web开发中过滤一些我们指定的url 那么它能帮我们过滤什么呢? 那功能可就多了: 比如过拦截掉我们不需要的接口请求 修改请求(reques

  • SpringBoot 过滤器、拦截器、监听器对比及使用场景分析

    一.关系图理解 二.区别 1.过滤器 过滤器是在web应用启动的时候初始化一次, 在web应用停止的时候销毁 可以对请求的URL进行过滤, 对敏感词过滤 挡在拦截器的外层 实现的是 javax.servlet.Filter 接口 ,是 Servlet 规范的一部分 在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后 依赖Web容器 会多次执行 过滤器简介 过滤器的英文名称为 Filter, 是 Servlet 技术中最实用的技术.如同它的名字一样,过滤器

  • SpringBoot实现过滤器拦截器的耗时对比

    目录 过滤器的方式 拦截器的方式 三种方式 下面为大家一一对应 过滤器的方式 拦截器的方式 过滤器的方式 这种方式简单点 但是可配置性不高 注意:一定得扫描到spring容器中 创建一个类 实现 filter接口 init:该方法是对filter对象进行初始化的方法,仅在容器初始化filter对象结束后被调用一次,参数FilterConfig可以获得filter的初始化参数: doFilter:可以对request和response进行<u>预处理</u>.其中FilterChai

  • springboot config 拦截器使用方法实例详解

    本文介绍Spring-Boot中使用拦截器,一般在拦截器中处理跨域处理,允许跨域访问项目,拦截器使用详细资料请查阅官网. 实现自定义拦截器步骤: 1.创建一个类并实现HandlerInterceptor接口. 2.创建一个Java类继承WebMvcConfigurerAdapter,并重写 addInterceptors 方法. 2.将自定义的拦截器交由spring管理,然后将对像手动添加到拦截器链中(在addInterceptors方法中添加). 创建拦截器类 package com.exam

  • springboot注册拦截器所遇到的问题

    问题1 springboot注册拦截器过滤器方法 注册拦截器:在启动类中注册bean @EnableWebMvc @Configuration static class MvcConfigurer implements WebMvcConfigurer { //在拦截器中需要使用这个bean,如果直接在拦截器中注入的话会失败,所以选择有参构造的方式传入 @Autowired CacheService cacheService; @Override public void addIntercept

  • 教你用Springboot实现拦截器获取header内容

    分析 既然需要动态获取那么只有两种方式:要么每次下游请求过来时从请求头中获取,要么定义统一的拦截器自动获取. 实现 那么我们就先来实现一下吧. 第一种比较简单,直接使用springboot获取请求头的方式,从controller方法入口处使用: @RequestHeader(value = "xxxx",required = false) String appUser的方式获取请求头 代码如下: @RequestMapping(name = "获取用户详情信息",v

  • springboot实现拦截器的3种方式及异步执行的思考

    目录 springboot 拦截器 springboot 入门案例 maven 引入 启动类 定义 Controller 拦截器定义 基于 Aspect 基于 HandlerInterceptor 基于 ResponseBodyAdvice 测试 异步执行 定义异步线程池 异步执行的 Controller 思考 测试 反思 springboot 拦截器 实际项目中,我们经常需要输出请求参数,响应结果,方法耗时,统一的权限校验等. 本文首先为大家介绍 HTTP 请求中三种常见的拦截实现,并且比较一

  • 浅谈Springboot实现拦截器的两种方式

    目录 一.拦截器方式 1.配置HandlerInterceptor 2.注册拦截器 3.使用拦截器的坑 二.过滤器方式 1.实现Filter接口 2.使用过滤器需要注意的 实现过滤请求有两种方式: 一种就是用拦截器,一种就是过滤器 拦截器相对来说比较专业,而过滤器虽然不专业但是也能完成基本的拦截请求要求. 一.拦截器方式 1.配置HandlerInterceptor 下面这个也是我们公司项目拦截器的写法,总体来说感觉还不错,我就记录了下来. 利用了一个静态Pattern变量存储不走拦截器的路径,

  • 解决SpringBoot自定义拦截器和跨域配置冲突的问题

    目录 SpringBoot自定义拦截器和跨域配置冲突 技术栈 问题引出 原代码 新代码 SpringBoot 拦截器和addCorsMappings冲突 SpringBoot自定义拦截器和跨域配置冲突 技术栈 vue-cli3,springboot 2.3.2.RELEASE 问题引出 在做毕业设计过程中用到了自定义拦截器验证登录.同时在springboot配置类中设置了跨域问题,出现跨域失败的情况. 原代码 @Configuration public class WebConfig exten

  • SpringBoot项目拦截器获取Post方法的请求body实现

    1). 存在问题流只能读取一次 2). 目标多次读取流 3). 解决方法创建包装类 4). RequestWrapper package com.mazaiting.redeye.wrapper;   import com.mazaiting.redeye.utils.StreamUtil; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory;   import jav

随机推荐