美文网首页SpringBootspring 整合使用面试
SpringBoot 请求拦截--filter、intercep

SpringBoot 请求拦截--filter、intercep

作者: 大程子的技术成长路 | 来源:发表于2021-05-13 10:20 被阅读0次

    1.场景

    web程序中,对用户请求,经常会对请求进行拦截处理,常用的处理方式如下:

    • Filter
    • Interceptor
    • AOP

    在此基于SpringBoot的web程序,进行这三种拦截方式的说明。

    2.区别

    三种拦截方式的区别如下:

    依赖 Servlet容器 Spring Web Spring
    基于实现 回调机制 反射机制(AOP思想) 动态代理
    类别 Filter Interceptor AOP
    实现方式 实现接口Filter 实现接口HandlerInterceptor 注解@Aspect
    作用范围 所有URL请求(可过滤) 所有Controller的action 包括自己定义的和其他组件定义的 spring的bean(可过滤)
    可操作数据 原始Http请求信息: ServletRequest request, ServletResponse response (1)Http请求信息: HttpServletRequest request, HttpServletResponse response, (2)springMvc执行的方法信息: HandlerMethod handlerMethod (3)返回结果(执行Action方法后,不报错): ModelAndView modelAndView (4)异常信息(执行Action方法后): Exception ex 请求参数 返回结果 异常信息
    不可操作数据 执行方法相关信息 ResponseBody的返回结果 http请求信息
    相关方法 doFilter preHandle postHandle afterCompletion@ @Aspect @Pointcut @Before @After @Around
    用途 字符编码, 鉴权操作, 防重复提交 记录执行时间, 脱敏信息、 过滤敏感词、 多租户切换 ...... 字符编码 鉴权操作 防重复提交 异常记录 ...... 日志记录 异常记录 数据源切换 请求埋点 ......

    3.请求顺序

    基于SpringBoot的web程序,Filter、Interceptor、Aop的请求顺序如下:

    Filter -> Interceptor ->AOP -> Controller

    image-20210512172914548.png

    4.版本

    4.1 maven依赖

    Filter和Interceptor有spring-boot-starter-web依赖即可:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    

    AOP依赖的aspectJ需要额外的maven依赖:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
        <version>2.2.9.RELEASE</version>
    </dependency>
    

    4.2 测试Controller

    package com.wangcp.intercept.controller;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
    * 测试类
    * @author wangcp
    * @date 2021/05/12 15:45
    **/
    @RestController
    @RequestMapping(value = "/my")
    public class MyController {
    
        @GetMapping(value = "/test")
        public Map<String,Object> test(String userName , String age){
            String message = "[Controller Action]:userName=" + userName + ";age=" + age;
            System.out.println(message);
            Map<String,Object> map = new HashMap<>();
            map.put("success",true);
            map.put("message",message);
            return map;
        }
    
    }
    

    5.Filter代码实现

    5.1 说明

    1.实现接口

    实现接口:javax.servlet.Filter

    2.核心方法

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {}
    

    5.2 定义

    1.定义Filter

    package com.wangcp.intercept.filter;
    
    
    import javax.servlet.*;
    import java.io.IOException;
    import java.util.Date;
    
    /**
    * 计算执行时间Filter
    * @author wangcp
    * @date 2021/05/12 15:50
    **/
    public class TimerFilter implements Filter {
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            long begin = new Date().getTime();
            System.out.println("[Filter-Time]:进入Filter");
            // 执行servlet方法(如拦截请求,不执行servlet,可不执行此方法)
            filterChain.doFilter(servletRequest , servletResponse);
            long end = new Date().getTime();
            System.out.println("[Filter-Time]:结束Filter,共" + (end - begin) + "毫秒");
        }
    }
    

    2.配置

    package com.wangcp.intercept.filter.config;
    
    import com.wangcp.intercept.filter.TimerFilter;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.Arrays;
    
    /**
    * filter配置类
    * @author wangcp
    * @date 2021/05/12 15:54
    **/
    @Configuration
    public class WebFilterConfig {
    
        @Bean
        public FilterRegistrationBean timerFilter(){
            FilterRegistrationBean registrationBean = new FilterRegistrationBean();
            // 设置:实现类
            registrationBean.setFilter(new TimerFilter());
            // 设置:UrlPatterns (要拦截的url)
            registrationBean.setUrlPatterns(Arrays.asList("/*"));
            // 设置:优先级
            registrationBean.setOrder(1);
            return registrationBean;
        }
    
    }
    

    5.3 测试

    1.测试请求

    http://localhost:8080/my/test?userName=wangcp&age=18
    

    2.输出结果

    [Filter-Time]:进入Filter
    [Controller Action]:userName=wangcp;age=18
    [Filter-Time]:结束Filter,共40毫秒
    

    5.4 配置顺序

    // 设置:优先级
    registrationBean.setOrder(1);
    

    6.HandlerInterceptor 代码实现

    6.1 说明

    1.实现接口

    实现接口:org.springframework.web.servlet.HandlerInterceptor

    2.核心方法

    各方法详细介绍可查看: https://blog.csdn.net/weixin_41767154/article/details/84648873

    // 调用Controller方法之前
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
    
    // 在当前这个Interceptor的preHandle方法返回值为true的时候才会执行
    void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable ModelAndView modelAndView) throws Exception;
    
    // 也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行
    void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable Exception ex) throws Exception;
    

    6.2 定义

    1.定义Interceptor

    package com.wangcp.intercept.interceptor;
    
    import com.alibaba.fastjson.JSONObject;
    import org.springframework.stereotype.Component;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.PrintWriter;
    
    /**
    * 鉴权拦截器
    * @author wangcp
    * @date 2021/05/12 16:05
     * https://blog.csdn.net/weixin_41767154/article/details/84648873
    **/
    @Component
    public class AuthInterceptor implements HandlerInterceptor {
    
        /**
        * 调用Controller方法之前
        * @author wangcp
        * @date 2021/05/12 16:06
         * @param request
         * @param response
         * @param handler
        * @return boolean
        */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("[Interceptor-auth]:进入preHandle");
            if(handler instanceof HandlerMethod){
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                System.out.println("[Interceptor-auth]:访问信息=" + handlerMethod.getShortLogMessage());
                // 获取head鉴权信息
                String sign = request.getHeader("sign");
                if (!"123456".equals(sign)) {
                    // 鉴权不通过
                    response.setCharacterEncoding("utf-8");
                    response.setContentType("application/json; charset=utf-8");
                    PrintWriter writer = response.getWriter();
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("success", false);
                    jsonObject.put("message", "鉴权失败");
                    writer.write(jsonObject.toJSONString());
                    writer.flush();
                    writer.close();
                    System.out.println("[Interceptor-auth]:----------鉴权不通过----------");
                    System.out.println("[Interceptor-auth]:结束preHandle");
                    return false;
                } else {
                    // 鉴权通过
                    System.out.println("[Interceptor-auth]:----------鉴权通过----------");
                    System.out.println("[Interceptor-auth]:结束preHandle");
                    return true;
                }
            }
            System.out.println("[Interceptor-auth]:结束preHandle");
            // 返回true为通过校验,返回false为不通过校验
            return true;
        }
    
        /**
        * 在当前这个Interceptor的preHandle方法返回值为true的时候才会执行
         * 执行时机:在DispatcherServlet进行视图的渲染之前执行,也就是说在这个方法中你可以对ModelAndView进行操作
        * @author wangcp
        * @date 2021/05/12 16:07
         * @param request
         * @param response
         * @param handler
         * @param modelAndView
        * @return void
        */
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("[Interceptor-auth]:postHandle ModelAndView=" + JSONObject.toJSONString(modelAndView));
        }
    
        /**
         * 也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行
         * 执行时机:该方法将在整个请求完成之后,也就是DispatcherServlet渲染了视图执行
         * (这个方法的主要作用是用于清理资源的)
        * @author wangcp
        * @date 2021/05/12 16:07
         * @param request
         * @param response
         * @param handler
         * @param ex
        * @return void
        */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("[Interceptor-auth]:afterCompletion Exception=" + JSONObject.toJSONString(ex));
        }
    }
    

    2.配置

    package com.wangcp.intercept.interceptor.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    /**
    * WEB统一配置
    * @author wangcp
    * @date 2021/05/12 16:20
    **/
    @Component
    public class GlobalWebMvcConfigurer implements WebMvcConfigurer {
    
        @Autowired
        private HandlerInterceptor authInterceptor;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(authInterceptor).addPathPatterns("/**");
    
            // 支持定义多个PathPattern和excludePathPatterns
    //        registry.addInterceptor(authInterceptor).addPathPatterns("/xxx","/**").excludePathPatterns("/yyy","/zzz");
        }
    }
    

    6.3 测试

    1.正向测试

    请求:

    http://localhost:8080/my/test?userName=wangcp&age=18
    请求head:sign=123456
    

    输出结果:

    [Interceptor-auth]:进入preHandle
    [Interceptor-auth]:访问信息=com.wangcp.intercept.controller.MyController#test[2 args]
    [Interceptor-auth]:----------鉴权通过----------
    [Interceptor-auth]:结束preHandle
    [Controller Action]:userName=wangcp;age=18
    [Interceptor-auth]:postHandle ModelAndView=null
    [Interceptor-auth]:afterCompletion Exception=null
    

    请求结果:

    {
        "success": true,
        "message": "[Controller Action]:userName=wangcp;age=18"
    }
    

    2.逆向测试

    请求:

    http://localhost:8080/my/test?userName=wangcp&age=18
    请求head:sign=8888
    

    输出结果:

    [Interceptor-auth]:进入preHandle
    [Interceptor-auth]:访问信息=com.wangcp.intercept.controller.MyController#test[2 args]
    [Interceptor-auth]:----------鉴权不通过----------
    [Interceptor-auth]:结束preHandle
    

    请求结果:

    {
        "success": false,
        "message": "鉴权失败"
    }
    

    7.AOP代码实现

    7.1 说明

    相关注解

    各注解详细介绍可参考:https://blog.csdn.net/qq_45515432/article/details/104187326

    org.aspectj.lang.annotation.Aspect
    org.aspectj.lang.annotation.Pointcut
    org.aspectj.lang.annotation.Before
    org.aspectj.lang.annotation.After
    org.aspectj.lang.annotation.Around
    

    7.2 定义

    package com.wangcp.intercept.aop;
    
    import com.alibaba.fastjson.JSONObject;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    /**
    * AOP实现拦截请求
    * @author wangcp
    * @date 2021/05/12 16:34
     * https://blog.csdn.net/qq_45515432/article/details/104187326
    **/
    @Component
    @Order(1)
    @Aspect
    public class LogApp {
    
        @Pointcut("execution(public * com.wangcp..controller..*(..))")
        public void log() {
            
        }
    
        @Before("log()")
        public void doBefore(JoinPoint joinPoint) {
            System.out.println("[AOP-log]:Before");
        }
    
        @After("log()")
        public void doAfter(JoinPoint joinPoint) {
            System.out.println("[AOP-log]:After");
        }
    
        @Around("log()")
        public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("[AOP-log]:Around-进入");
            // 请求参数
            System.out.println("[AOP-log]:Around-请求参数="+ JSONObject.toJSONString(joinPoint.getArgs()));
    
            // 执行切面方法
            Object object = joinPoint.proceed();
    
            // 执行结果
            System.out.println("[AOP-log]:Around-执行结果="+ JSONObject.toJSONString(object));
            System.out.println("[AOP-log]:Around-结束");
            return object;
        }
    }
    

    7.3 测试

    1.测试请求

    http://localhost:8080/my/test?userName=wangcp&age=18
    

    2.输出结果

    [AOP-log]:Around-进入
    [AOP-log]:Around-请求参数=["wangcp","18"]
    [AOP-log]:Before
    [Controller Action]:userName=wangcp;age=18
    [AOP-log]:After
    [AOP-log]:Around-执行结果={"success":true,"message":"[Controller Action]:userName=wangcp;age=18"}
    [AOP-log]:Around-结束
    

    3.请求结果

    {"success":true,"message":"[Controller Action]:userName=wangcp;age=18"}
    

    8.汇总测试

    同时打开上述的Filter,Interceptor,AOP,一起来拦截请求。

    1.测试请求

    http://localhost:8080/my/test?userName=wangcp&age=18
    请求head:sign=123456
    

    2.输出结果

    [Filter-Time]:进入Filter
    [Interceptor-auth]:进入preHandle
    [Interceptor-auth]:访问信息=com.wangcp.intercept.controller.MyController#test[2 args]
    [Interceptor-auth]:----------鉴权通过----------
    [Interceptor-auth]:结束preHandle
    [AOP-log]:Around-进入
    [AOP-log]:Around-请求参数=["wangcp","18"]
    [AOP-log]:Before
    [Controller Action]:userName=wangcp;age=18
    [AOP-log]:After
    [AOP-log]:Around-执行结果={"success":true,"message":"[Controller Action]:userName=wangcp;age=18"}
    [AOP-log]:Around-结束
    [Interceptor-auth]:postHandle ModelAndView=null
    [Interceptor-auth]:afterCompletion Exception=null
    [Filter-Time]:结束Filter,共1毫秒
    

    输出顺序与文章开头请求顺序讲解一致。

    3.请求结果

    {
        "success": true,
        "message": "[Controller Action]:userName=wangcp;age=18"
    }
    

    相关文章

      网友评论

        本文标题:SpringBoot 请求拦截--filter、intercep

        本文链接:https://www.haomeiwen.com/subject/ubytjltx.html