1.場(chǎng)景
web程序中刮萌,對(duì)用戶請(qǐng)求驮配,經(jīng)常會(huì)對(duì)請(qǐng)求進(jìn)行攔截處理,常用的處理方式如下:
- Filter
- Interceptor
- AOP
在此基于SpringBoot的web程序着茸,進(jìn)行這三種攔截方式的說明僧凤。
2.區(qū)別
三種攔截方式的區(qū)別如下:
依賴 | Servlet容器 | Spring Web | Spring |
---|---|---|---|
基于實(shí)現(xiàn) | 回調(diào)機(jī)制 | 反射機(jī)制(AOP思想) | 動(dòng)態(tài)代理 |
類別 | Filter | Interceptor | AOP |
實(shí)現(xiàn)方式 | 實(shí)現(xiàn)接口Filter | 實(shí)現(xiàn)接口HandlerInterceptor | 注解@Aspect |
作用范圍 | 所有URL請(qǐng)求(可過濾) | 所有Controller的action 包括自己定義的和其他組件定義的 | spring的bean(可過濾) |
可操作數(shù)據(jù) | 原始Http請(qǐng)求信息: ServletRequest request, ServletResponse response | (1)Http請(qǐng)求信息: HttpServletRequest request, HttpServletResponse response, (2)springMvc執(zhí)行的方法信息: HandlerMethod handlerMethod (3)返回結(jié)果(執(zhí)行Action方法后,不報(bào)錯(cuò)): ModelAndView modelAndView (4)異常信息(執(zhí)行Action方法后): Exception ex | 請(qǐng)求參數(shù) 返回結(jié)果 異常信息 |
不可操作數(shù)據(jù) | 執(zhí)行方法相關(guān)信息 | ResponseBody的返回結(jié)果 | http請(qǐng)求信息 |
相關(guān)方法 | doFilter | preHandle postHandle afterCompletion@ | @Aspect @Pointcut @Before @After @Around |
用途 | 字符編碼元扔, 鑒權(quán)操作, 防重復(fù)提交 記錄執(zhí)行時(shí)間旋膳, 脫敏信息澎语、 過濾敏感詞、 多租戶切換 ...... | 字符編碼 鑒權(quán)操作 防重復(fù)提交 異常記錄 ...... | 日志記錄 異常記錄 數(shù)據(jù)源切換 請(qǐng)求埋點(diǎn) ...... |
3.請(qǐng)求順序
基于SpringBoot的web程序,F(xiàn)ilter擅羞、Interceptor尸变、Aop的請(qǐng)求順序如下:
Filter -> Interceptor ->AOP -> Controller
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 測(cè)試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;
/**
* 測(cè)試類
* @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代碼實(shí)現(xiàn)
5.1 說明
1.實(shí)現(xiàn)接口
實(shí)現(xiàn)接口: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;
/**
* 計(jì)算執(zhí)行時(shí)間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]:進(jìn)入Filter");
// 執(zhí)行servlet方法(如攔截請(qǐng)求减俏,不執(zhí)行servlet召烂,可不執(zhí)行此方法)
filterChain.doFilter(servletRequest , servletResponse);
long end = new Date().getTime();
System.out.println("[Filter-Time]:結(jié)束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();
// 設(shè)置:實(shí)現(xiàn)類
registrationBean.setFilter(new TimerFilter());
// 設(shè)置:UrlPatterns (要攔截的url)
registrationBean.setUrlPatterns(Arrays.asList("/*"));
// 設(shè)置:優(yōu)先級(jí)
registrationBean.setOrder(1);
return registrationBean;
}
}
5.3 測(cè)試
1.測(cè)試請(qǐng)求
http://localhost:8080/my/test?userName=wangcp&age=18
2.輸出結(jié)果
[Filter-Time]:進(jìn)入Filter
[Controller Action]:userName=wangcp娃承;age=18
[Filter-Time]:結(jié)束Filter奏夫,共40毫秒
5.4 配置順序
// 設(shè)置:優(yōu)先級(jí)
registrationBean.setOrder(1);
6.HandlerInterceptor 代碼實(shí)現(xiàn)
6.1 說明
1.實(shí)現(xiàn)接口
實(shí)現(xiàn)接口:org.springframework.web.servlet.HandlerInterceptor
2.核心方法
各方法詳細(xì)介紹可查看: https://blog.csdn.net/weixin_41767154/article/details/84648873
// 調(diào)用Controller方法之前
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
// 在當(dāng)前這個(gè)Interceptor的preHandle方法返回值為true的時(shí)候才會(huì)執(zhí)行
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable ModelAndView modelAndView) throws Exception;
// 也是需要當(dāng)前對(duì)應(yīng)的Interceptor的preHandle方法的返回值為true時(shí)才會(huì)執(zhí)行
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;
/**
* 鑒權(quán)攔截器
* @author wangcp
* @date 2021/05/12 16:05
* https://blog.csdn.net/weixin_41767154/article/details/84648873
**/
@Component
public class AuthInterceptor implements HandlerInterceptor {
/**
* 調(diào)用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]:進(jìn)入preHandle");
if(handler instanceof HandlerMethod){
HandlerMethod handlerMethod = (HandlerMethod) handler;
System.out.println("[Interceptor-auth]:訪問信息=" + handlerMethod.getShortLogMessage());
// 獲取head鑒權(quán)信息
String sign = request.getHeader("sign");
if (!"123456".equals(sign)) {
// 鑒權(quán)不通過
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", "鑒權(quán)失敗");
writer.write(jsonObject.toJSONString());
writer.flush();
writer.close();
System.out.println("[Interceptor-auth]:----------鑒權(quán)不通過----------");
System.out.println("[Interceptor-auth]:結(jié)束preHandle");
return false;
} else {
// 鑒權(quán)通過
System.out.println("[Interceptor-auth]:----------鑒權(quán)通過----------");
System.out.println("[Interceptor-auth]:結(jié)束preHandle");
return true;
}
}
System.out.println("[Interceptor-auth]:結(jié)束preHandle");
// 返回true為通過校驗(yàn),返回false為不通過校驗(yàn)
return true;
}
/**
* 在當(dāng)前這個(gè)Interceptor的preHandle方法返回值為true的時(shí)候才會(huì)執(zhí)行
* 執(zhí)行時(shí)機(jī):在DispatcherServlet進(jìn)行視圖的渲染之前執(zhí)行历筝,也就是說在這個(gè)方法中你可以對(duì)ModelAndView進(jìn)行操作
* @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));
}
/**
* 也是需要當(dāng)前對(duì)應(yīng)的Interceptor的preHandle方法的返回值為true時(shí)才會(huì)執(zhí)行
* 執(zhí)行時(shí)機(jī):該方法將在整個(gè)請(qǐng)求完成之后酗昼,也就是DispatcherServlet渲染了視圖執(zhí)行
* (這個(gè)方法的主要作用是用于清理資源的)
* @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統(tǒng)一配置
* @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("/**");
// 支持定義多個(gè)PathPattern和excludePathPatterns
// registry.addInterceptor(authInterceptor).addPathPatterns("/xxx","/**").excludePathPatterns("/yyy","/zzz");
}
}
6.3 測(cè)試
1.正向測(cè)試
請(qǐng)求:
http://localhost:8080/my/test?userName=wangcp&age=18
請(qǐng)求head:sign=123456
輸出結(jié)果:
[Interceptor-auth]:進(jìn)入preHandle
[Interceptor-auth]:訪問信息=com.wangcp.intercept.controller.MyController#test[2 args]
[Interceptor-auth]:----------鑒權(quán)通過----------
[Interceptor-auth]:結(jié)束preHandle
[Controller Action]:userName=wangcp;age=18
[Interceptor-auth]:postHandle ModelAndView=null
[Interceptor-auth]:afterCompletion Exception=null
請(qǐng)求結(jié)果:
{
"success": true,
"message": "[Controller Action]:userName=wangcp梳猪;age=18"
}
2.逆向測(cè)試
請(qǐng)求:
http://localhost:8080/my/test?userName=wangcp&age=18
請(qǐng)求head:sign=8888
輸出結(jié)果:
[Interceptor-auth]:進(jìn)入preHandle
[Interceptor-auth]:訪問信息=com.wangcp.intercept.controller.MyController#test[2 args]
[Interceptor-auth]:----------鑒權(quán)不通過----------
[Interceptor-auth]:結(jié)束preHandle
請(qǐng)求結(jié)果:
{
"success": false,
"message": "鑒權(quán)失敗"
}
7.AOP代碼實(shí)現(xiàn)
7.1 說明
相關(guān)注解
各注解詳細(xì)介紹可參考: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實(shí)現(xiàn)攔截請(qǐng)求
* @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-進(jìn)入");
// 請(qǐng)求參數(shù)
System.out.println("[AOP-log]:Around-請(qǐng)求參數(shù)="+ JSONObject.toJSONString(joinPoint.getArgs()));
// 執(zhí)行切面方法
Object object = joinPoint.proceed();
// 執(zhí)行結(jié)果
System.out.println("[AOP-log]:Around-執(zhí)行結(jié)果="+ JSONObject.toJSONString(object));
System.out.println("[AOP-log]:Around-結(jié)束");
return object;
}
}
7.3 測(cè)試
1.測(cè)試請(qǐng)求
http://localhost:8080/my/test?userName=wangcp&age=18
2.輸出結(jié)果
[AOP-log]:Around-進(jìn)入
[AOP-log]:Around-請(qǐng)求參數(shù)=["wangcp","18"]
[AOP-log]:Before
[Controller Action]:userName=wangcp麻削;age=18
[AOP-log]:After
[AOP-log]:Around-執(zhí)行結(jié)果={"success":true,"message":"[Controller Action]:userName=wangcp;age=18"}
[AOP-log]:Around-結(jié)束
3.請(qǐng)求結(jié)果
{"success":true,"message":"[Controller Action]:userName=wangcp春弥;age=18"}
8.匯總測(cè)試
同時(shí)打開上述的Filter呛哟,Interceptor,AOP匿沛,一起來攔截請(qǐng)求扫责。
1.測(cè)試請(qǐng)求
http://localhost:8080/my/test?userName=wangcp&age=18
請(qǐng)求head:sign=123456
2.輸出結(jié)果
[Filter-Time]:進(jìn)入Filter
[Interceptor-auth]:進(jìn)入preHandle
[Interceptor-auth]:訪問信息=com.wangcp.intercept.controller.MyController#test[2 args]
[Interceptor-auth]:----------鑒權(quán)通過----------
[Interceptor-auth]:結(jié)束preHandle
[AOP-log]:Around-進(jìn)入
[AOP-log]:Around-請(qǐng)求參數(shù)=["wangcp","18"]
[AOP-log]:Before
[Controller Action]:userName=wangcp;age=18
[AOP-log]:After
[AOP-log]:Around-執(zhí)行結(jié)果={"success":true,"message":"[Controller Action]:userName=wangcp俺祠;age=18"}
[AOP-log]:Around-結(jié)束
[Interceptor-auth]:postHandle ModelAndView=null
[Interceptor-auth]:afterCompletion Exception=null
[Filter-Time]:結(jié)束Filter公给,共1毫秒
輸出順序與文章開頭請(qǐng)求順序講解一致。
3.請(qǐng)求結(jié)果
{
"success": true,
"message": "[Controller Action]:userName=wangcp蜘渣;age=18"
}