前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >请求拦截之filter、interceptor、aop

请求拦截之filter、interceptor、aop

原创
作者头像
ruochen
发布2021-11-26 13:10:08
2.1K0
发布2021-11-26 13:10:08
举报
文章被收录于专栏:若尘的技术专栏

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

请求顺序.png

4 版本

4.1 maven依赖

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

代码语言:txt
复制
<dependency>
代码语言:txt
复制
    <groupId>org.springframework.boot</groupId>
代码语言:txt
复制
    <artifactId>spring-boot-starter-web</artifactId>
代码语言:txt
复制
    <version>2.2.9.RELEASE</version>
代码语言:txt
复制
</dependency>

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

代码语言:txt
复制
<dependency>
代码语言:txt
复制
    <groupId>org.springframework.boot</groupId>
代码语言:txt
复制
    <artifactId>spring-boot-starter-aop</artifactId>
代码语言:txt
复制
    <version>2.2.9.RELEASE</version>
代码语言:txt
复制
</dependency>
4.2 测试Controller
代码语言:txt
复制
package com.pdd.module.lanjie.controller;
代码语言:txt
复制
import org.springframework.web.bind.annotation.RequestMapping;
代码语言:txt
复制
import org.springframework.web.bind.annotation.RestController;
代码语言:txt
复制
import java.util.HashMap;
代码语言:txt
复制
import java.util.Map;
代码语言:txt
复制
@RestController
代码语言:txt
复制
@RequestMapping("/my")
代码语言:txt
复制
public class MyController {
代码语言:txt
复制
    @RequestMapping("test")
代码语言:txt
复制
    public Map<String,Object> test(String userName, String age) {
代码语言:txt
复制
        String message = "[Controller Action]:userName=" + userName + ";age=" + age;
代码语言:txt
复制
        System.out.println(message);
代码语言:txt
复制
        Map<String,Object> map = new HashMap<>();
代码语言:txt
复制
        map.put("success",true);
代码语言:txt
复制
        map.put("message",message);
代码语言:txt
复制
        return map;
代码语言:txt
复制
    }
代码语言:txt
复制
}

5 Filter代码实现

5.1 说明

(1)实现接口

实现接口:javax.servlet.Filter

(2)核心方法

代码语言:txt
复制
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;
5.2 定义

(1) 定义Filter

代码语言:txt
复制
package com.pdd.module.lanjie.filter;
代码语言:txt
复制
import javax.servlet.*;
代码语言:txt
复制
import java.io.IOException;
代码语言:txt
复制
import java.util.Date;
代码语言:txt
复制
/**
代码语言:txt
复制
 * 计算执行时间Filter
 */
public class TimerFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        long begin = new Date().getTime();
        System.out.println("[Filter-Time]:进入Filter");
        // 执行servlet方法(如拦截请求,不执行Servlet,可不执行此方法)
        chain.doFilter(request, response);
        long end = new Date().getTime();
        System.out.println("[Filter-Time]:结束Filter,共" + (end - begin) + "毫秒");
    }
}

(2)配置

代码语言:txt
复制
package com.pdd.module.lanjie.filter.config;
代码语言:txt
复制
import com.pdd.module.lanjie.filter.TimerFilter;
代码语言:txt
复制
import org.springframework.boot.web.servlet.FilterRegistrationBean;
代码语言:txt
复制
import org.springframework.context.annotation.Bean;
代码语言:txt
复制
import org.springframework.context.annotation.Configuration;
代码语言:txt
复制
import java.util.Arrays;
代码语言:txt
复制
@Configuration
代码语言:txt
复制
public class WebFilterConfig {
代码语言:txt
复制
    @Bean
代码语言:txt
复制
    public FilterRegistrationBean timerFilter() {
代码语言:txt
复制
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
代码语言:txt
复制
        // 设置:实现类
代码语言:txt
复制
        registrationBean.setFilter(new TimerFilter());
代码语言:txt
复制
        // 设置:UrlPatterns
代码语言:txt
复制
        registrationBean.setUrlPatterns(Arrays.asList("/*"));
代码语言:txt
复制
        // 设置:优先级
代码语言:txt
复制
        registrationBean.setOrder(1);
代码语言:txt
复制
        return registrationBean;
代码语言:txt
复制
    }
代码语言:txt
复制
}
5.3 测试

(1)测试请求

代码语言:txt
复制
http://localhost:8080/my/test?userName=张三&age=23

(2)输出结果

代码语言:txt
复制
[Filter-Time]:进入Filter
代码语言:txt
复制
[Controller Action]:userName=张三;age=23
代码语言:txt
复制
[Filter-Time]:结束Filter,共90毫秒
5.4 配置顺序
代码语言:txt
复制
// 设置:优先级
代码语言:txt
复制
registrationBean.setOrder(1);

6 HandlerInterceptor代码实现

6.1 说明

(1)实现接口

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

(2)核心方法

代码语言:txt
复制
// 调用Controller方法之前
代码语言:txt
复制
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
代码语言:txt
复制
// 调用Controller方法之后(不抛出异常)
代码语言:txt
复制
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable ModelAndView modelAndView) throws Exception;
代码语言:txt
复制
// 调用Controller方法之后(无论是否抛出异常)
代码语言:txt
复制
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable Exception ex) throws Exception;
6.2 定义

(1)定义Interceptor

代码语言:txt
复制
package com.pdd.module.lanjie.interceptor;
代码语言:txt
复制
import com.alibaba.fastjson.JSONObject;
代码语言:txt
复制
import org.springframework.stereotype.Component;
代码语言:txt
复制
import org.springframework.web.method.HandlerMethod;
代码语言:txt
复制
import org.springframework.web.servlet.HandlerInterceptor;
代码语言:txt
复制
import org.springframework.web.servlet.ModelAndView;
代码语言:txt
复制
import javax.servlet.http.HttpServletRequest;
代码语言:txt
复制
import javax.servlet.http.HttpServletResponse;
代码语言:txt
复制
import java.io.PrintWriter;
代码语言:txt
复制
/**
代码语言:txt
复制
 * 鉴权Interceptor
 */
@Component
public class AuthInterceptor implements HandlerInterceptor {
    
代码语言:txt
复制
    @Override
代码语言:txt
复制
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
代码语言:txt
复制
        System.out.println("[Interceptor-auth]:进入preHandle");
代码语言:txt
复制
        if (handler instanceof HandlerMethod) {
代码语言:txt
复制
            HandlerMethod handlerMethod = (HandlerMethod) handler;
代码语言:txt
复制
            System.out.println("[Interceptor-auth]:访问信息=" + handlerMethod.getShortLogMessage());
代码语言:txt
复制
            // 获取head鉴权信息
代码语言:txt
复制
            String sign = request.getHeader("sign");
代码语言:txt
复制
            if (!"123456".equals(sign)) {
代码语言:txt
复制
                // 鉴权不通过
代码语言:txt
复制
                response.setCharacterEncoding("utf-8");
代码语言:txt
复制
                response.setContentType("application/json; charset=utf-8");
代码语言:txt
复制
                PrintWriter writer = response.getWriter();
代码语言:txt
复制
                JSONObject jsonObject = new JSONObject();
代码语言:txt
复制
                jsonObject.put("success", false);
代码语言:txt
复制
                jsonObject.put("message", "鉴权失败");
代码语言:txt
复制
                writer.write(jsonObject.toJSONString());
代码语言:txt
复制
                writer.flush();
代码语言:txt
复制
                writer.close();
代码语言:txt
复制
                System.out.println("[Interceptor-auth]:----------鉴权不通过----------");
代码语言:txt
复制
                System.out.println("[Interceptor-auth]:结束preHandle");
代码语言:txt
复制
                return false;
代码语言:txt
复制
            } else {
代码语言:txt
复制
                // 鉴权通过
代码语言:txt
复制
                System.out.println("[Interceptor-auth]:----------鉴权通过----------");
代码语言:txt
复制
                System.out.println("[Interceptor-auth]:结束preHandle");
代码语言:txt
复制
                return true;
代码语言:txt
复制
            }
代码语言:txt
复制
        }
代码语言:txt
复制
        System.out.println("[Interceptor-auth]:结束preHandle");
代码语言:txt
复制
        // 返回true为通过校验,返回false为不通过校验
代码语言:txt
复制
        return true;
代码语言:txt
复制
    }
代码语言:txt
复制
    @Override
代码语言:txt
复制
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
代码语言:txt
复制
        System.out.println("[Interceptor-auth]:postHandle ModelAndView=" + JSONObject.toJSONString(modelAndView));
代码语言:txt
复制
    }
代码语言:txt
复制
    @Override
代码语言:txt
复制
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
代码语言:txt
复制
        System.out.println("[Interceptor-auth]:afterCompletion Exception=" + JSONObject.toJSONString(ex));
代码语言:txt
复制
    }
代码语言:txt
复制
}

(2)配置

代码语言:txt
复制
package com.pdd.module.lanjie.interceptor.config;
代码语言:txt
复制
import org.springframework.stereotype.Component;
代码语言:txt
复制
import org.springframework.web.servlet.HandlerInterceptor;
代码语言:txt
复制
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
代码语言:txt
复制
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
代码语言:txt
复制
import javax.annotation.Resource;
代码语言:txt
复制
/**
代码语言:txt
复制
 * WEB统一配置
 */
@Component
public class GlobalWebMvcConfigurer implements WebMvcConfigurer {
    
代码语言:txt
复制
    @Resource
代码语言:txt
复制
    private HandlerInterceptor authInterceptor;
代码语言:txt
复制
    @Override
代码语言:txt
复制
    public void addInterceptors(InterceptorRegistry registry) {
代码语言:txt
复制
        // 可按照顺序定义多个
代码语言:txt
复制
        registry.addInterceptor(authInterceptor).addPathPatterns("/**");
代码语言:txt
复制
        // 支持定义多个PathPattern和excludePathPatterns
代码语言:txt
复制
        //registry.addInterceptor(xxxInterceptor).addPathPatterns("/xxx","/**").excludePathPatterns("/yyy","/zzz");
代码语言:txt
复制
    }
代码语言:txt
复制
}
6.3 测试
6.3.1 正向测试

(1)测试请求

代码语言:txt
复制
http://localhost:8080/my/test?userName=张三&age=23
代码语言:txt
复制
请求head:sign=123456

(2)输出结果

  • 控制台输出
代码语言:txt
复制
[Interceptor-auth]:进入preHandle
代码语言:txt
复制
[Interceptor-auth]:访问信息=com.pdd.module.lanjie.controller.MyController#test[2 args]
代码语言:txt
复制
[Interceptor-auth]:----------鉴权通过----------
代码语言:txt
复制
[Interceptor-auth]:结束preHandle
代码语言:txt
复制
[Controller Action]:userName=张三;age=23
代码语言:txt
复制
[Interceptor-auth]:postHandle ModelAndView=null
代码语言:txt
复制
[Interceptor-auth]:afterCompletion Exception=null
  • 请求结果
代码语言:txt
复制
{
代码语言:txt
复制
    "success": true,
代码语言:txt
复制
    "message": "[Controller Action]:userName=张三;age=23"
代码语言:txt
复制
}
6.3.2 逆向测试

(1)测试请求

代码语言:txt
复制
http://localhost:8080/my/test?userName=张三&age=23
代码语言:txt
复制
请求head:无

(2)输出结果

  • 控制台输出
代码语言:txt
复制
[Interceptor-auth]:进入preHandle
代码语言:txt
复制
[Interceptor-auth]:访问信息=com.pdd.module.lanjie.controller.MyController#test[2 args]
代码语言:txt
复制
[Interceptor-auth]:----------鉴权不通过----------
代码语言:txt
复制
[Interceptor-auth]:结束preHandle
  • 请求结果
代码语言:txt
复制
{
代码语言:txt
复制
    "success": false,
代码语言:txt
复制
    "message": "鉴权失败"
代码语言:txt
复制
}
6.4 配置顺序
代码语言:txt
复制
public void addInterceptors(InterceptorRegistry registry) {
代码语言:txt
复制
    // 可按照顺序定义多个
代码语言:txt
复制
    registry.addInterceptor(xxxInterceptor).addPathPatterns(xxx);
代码语言:txt
复制
    registry.addInterceptor(yyyInterceptor).addPathPatterns(yyy);
代码语言:txt
复制
}

7 AOP代码实现

7.1 说明

相关注解

代码语言:txt
复制
org.aspectj.lang.annotation.Aspect
代码语言:txt
复制
org.aspectj.lang.annotation.Pointcut
代码语言:txt
复制
org.aspectj.lang.annotation.Before
代码语言:txt
复制
org.aspectj.lang.annotation.After
代码语言:txt
复制
org.aspectj.lang.annotation.Around
7.2 定义
代码语言:txt
复制
import com.alibaba.fastjson.JSONObject;
代码语言:txt
复制
import org.aspectj.lang.JoinPoint;
代码语言:txt
复制
import org.aspectj.lang.ProceedingJoinPoint;
代码语言:txt
复制
import org.aspectj.lang.annotation.*;
代码语言:txt
复制
import org.springframework.core.annotation.Order;
代码语言:txt
复制
import org.springframework.stereotype.Component;
代码语言:txt
复制
@Component
代码语言:txt
复制
@Order(1)
代码语言:txt
复制
@Aspect
代码语言:txt
复制
public class LogAop {
代码语言:txt
复制
    @Pointcut("execution(public * com.pdd..controller..*(..))")
代码语言:txt
复制
    public void log() {
代码语言:txt
复制
    }
代码语言:txt
复制
    @Before("log()")
代码语言:txt
复制
    public void doBefore(JoinPoint joinPoint) {
代码语言:txt
复制
        System.out.println("[AOP-log]:Before");
代码语言:txt
复制
    }
代码语言:txt
复制
    @After("log()")
代码语言:txt
复制
    public void doAfter(JoinPoint joinPoint) {
代码语言:txt
复制
        System.out.println("[AOP-log]:After");
代码语言:txt
复制
    }
代码语言:txt
复制
    @Around("log()")
代码语言:txt
复制
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
代码语言:txt
复制
        System.out.println("[AOP-log]:Around-进入");
代码语言:txt
复制
        // 请求参数
代码语言:txt
复制
        System.out.println("[AOP-log]:Around-请求参数="+ JSONObject.toJSONString(joinPoint.getArgs()));
代码语言:txt
复制
        // 执行切面方法
代码语言:txt
复制
        Object object = joinPoint.proceed();
代码语言:txt
复制
        // 执行结果
代码语言:txt
复制
        System.out.println("[AOP-log]:Around-执行结果="+ JSONObject.toJSONString(object));
代码语言:txt
复制
        System.out.println("[AOP-log]:Around-结束");
代码语言:txt
复制
        return object;
代码语言:txt
复制
    }
代码语言:txt
复制
}
7.3 测试

(1)测试请求

代码语言:txt
复制
http://localhost:8080/my/test?userName=张三&age=23

(2)后台日志

代码语言:txt
复制
[AOP-log]:Around-进入
代码语言:txt
复制
[AOP-log]:Around-请求参数=["张三","23"]
代码语言:txt
复制
[AOP-log]:Before
代码语言:txt
复制
[Controller Action]:userName=张三;age=23
代码语言:txt
复制
[AOP-log]:After
代码语言:txt
复制
[AOP-log]:Around-执行结果={"success":true,"message":"[Controller Action]:userName=张三;age=23"}
代码语言:txt
复制
[AOP-log]:Around-结束

(2)请求结果

代码语言:txt
复制
{"success":true,"message":"[Controller Action]:userName=张三;age=23"}
7.4 配置顺序

通过spring注解org.springframework.core.annotation.Order,来定义顺序。

代码语言:txt
复制
@Component
代码语言:txt
复制
@Order(1)
代码语言:txt
复制
@Aspect
代码语言:txt
复制
public class LogAop {
代码语言:txt
复制
    //......
代码语言:txt
复制
}

8 汇总测试

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

(1)测试请求

代码语言:txt
复制
http://localhost:8080/my/test?userName=张三&age=23
代码语言:txt
复制
请求head:sign=123456

(2)输出结果

  • 控制台输出
代码语言:txt
复制
[Filter-Time]:进入Filter
代码语言:txt
复制
[Interceptor-auth]:进入preHandle
代码语言:txt
复制
[Interceptor-auth]:访问信息=com.pdd.module.lanjie.controller.MyController#test[2 args]
代码语言:txt
复制
[Interceptor-auth]:----------鉴权通过----------
代码语言:txt
复制
[Interceptor-auth]:结束preHandle
代码语言:txt
复制
[AOP-log]:Around-进入
代码语言:txt
复制
[AOP-log]:Around-请求参数=["张三","23"]
代码语言:txt
复制
[AOP-log]:Before
代码语言:txt
复制
[Controller Action]:userName=张三;age=23
代码语言:txt
复制
[AOP-log]:After
代码语言:txt
复制
[AOP-log]:Around-执行结果={"success":true,"message":"[Controller Action]:userName=张三;age=23"}
代码语言:txt
复制
[AOP-log]:Around-结束
代码语言:txt
复制
[Interceptor-auth]:postHandle ModelAndView=null
代码语言:txt
复制
[Interceptor-auth]:afterCompletion Exception=null
代码语言:txt
复制
[Filter-Time]:结束Filter,共326毫秒

将输出内容,进行标注,和3 请求顺序,部分讲解的一致。

1615296824839.png

  • 请求结果
代码语言:txt
复制
{
代码语言:txt
复制
    "success": true,
代码语言:txt
复制
    "message": "[Controller Action]:userName=张三;age=23"
代码语言:txt
复制
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 场景
  • 2 区别
  • 3 请求顺序
  • 4 版本
    • 4.1 maven依赖
      • 4.2 测试Controller
      • 5 Filter代码实现
        • 5.1 说明
          • 5.2 定义
            • 5.3 测试
              • 5.4 配置顺序
              • 6 HandlerInterceptor代码实现
                • 6.1 说明
                  • 6.2 定义
                    • 6.3 测试
                      • 6.3.1 正向测试
                      • 6.3.2 逆向测试
                    • 6.4 配置顺序
                    • 7 AOP代码实现
                      • 7.1 说明
                        • 7.2 定义
                          • 7.3 测试
                            • 7.4 配置顺序
                            • 8 汇总测试
                            相关产品与服务
                            容器服务
                            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                            领券
                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档