SpringBoot 三種攔截http請求方式Filter,interceptor和aop。
這三種攔截方式的攔截順序是:filter—>Interceptor-->ControllerAdvice-->@Aspect -->Controller吩翻;
這三種方式的區(qū)別:
1.過濾器Filter可以拿到原始的HTTP請求和響應(yīng)的信息, 但是拿不到你真正處理請求方法的信息钥组,也就是方法的信息。
2.攔截器Interceptor可以拿到原始的HTTP請求和響應(yīng)的信息今瀑,也可以拿到你真正處理請求方法的信息程梦,但是拿不到傳進參數(shù)的那個值。
3.切片Aspect橘荠,既然Spring那么支持AOP屿附,可以拿到原始的HTTP請求和響應(yīng)的信息, 也可以拿到你真正處理請求方法的信息,也可以傳進參數(shù)的那個值哥童。
第一種方式:Filter
實現(xiàn)Filter接口
/**
* 自定義Filter
* 對請求的header 過濾token
*
* 過濾器Filter可以拿到原始的HTTP請求和響應(yīng)的信息拿撩,
* 但是拿不到你真正處理請求方法的信息,也就是方法的信息
*
* @Component 注解讓攔截器注入Bean如蚜,從而讓攔截器生效
* @WebFilter 配置攔截規(guī)則
*
* 攔截順序:filter—>Interceptor-->ControllerAdvice-->@Aspect -->Controller
*
*/
@Slf4j
@Component
@WebFilter(urlPatterns = {"/**"},filterName = "tokenAuthorFilter")
public class TokenFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("TokenFilter init {}",filterConfig.getFilterName());
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("TokenFilter doFilter 我攔截到了請求");
// log.info("TokenFilter doFilter",((HttpServletRequest)request).getHeader("token"));
chain.doFilter(request,response);//到下一個鏈
}
@Override
public void destroy() {
log.info("TokenFilter destroy");
}
}
第二種方式:攔截器 Interceptor
實現(xiàn) HandlerInterceptor 接口,然后配置進Spring影暴。
/**
* 自定義攔截器
* 自定義攔截器后错邦,需要配置進Spring
*
* 攔截器Interceptor可以拿到原始的HTTP請求和響應(yīng)的信息,
* 也可以拿到你真正處理請求方法的信息型宙,但是拿不到傳進參數(shù)的那個值撬呢。
*
*攔截順序:filter—>Interceptor-->ControllerAdvice-->@Aspect -->Controller
*/
@Slf4j
@Component
public class TokenInterceptor implements HandlerInterceptor {
/**
* 在訪問Controller某個方法之前這個方法會被調(diào)用。
* @param request
* @param response
* @param handler
* @return false則表示不執(zhí)行postHandle方法,true 表示執(zhí)行postHandle方法
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("Token Interceptor preHandle {}","");
String token = request.getHeader("token");
log.info("Token Interceptor preHandle token :{}",token);
log.info("Token Interceptor preHandle uri {}",request.getRequestURL().toString());
//spring boot 2.0對靜態(tài)資源也進行了攔截妆兑,當(dāng)攔截器攔截到請求之后魂拦,
// 但controller里并沒有對應(yīng)的請求時,該請求會被當(dāng)成是對靜態(tài)資源的請求搁嗓。
// 此時的handler就是 ResourceHttpRequestHandler芯勘,就會拋出上述錯誤。
if (handler instanceof HandlerMethod){
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
log.info("Token Interceptor preHandle getMethod {}",method.getName());
}else if(handler instanceof ResourceHttpRequestHandler){//靜態(tài)資源
ResourceHttpRequestHandler resourceHttpRequestHandler = (ResourceHttpRequestHandler) handler;
log.info("Token Interceptor preHandle getMethod {}",resourceHttpRequestHandler.getMediaTypes());
}
//false則表示不執(zhí)行postHandle方法,不執(zhí)行下一步chain鏈腺逛,直接返回response
return true;
}
/**
* 請求處理之后進行調(diào)用荷愕,但是在視圖被渲染之前(Controller方法調(diào)用之后)
* preHandle方法處理之后這個方法會被調(diào)用,如果控制器Controller出現(xiàn)了異常棍矛,則不會執(zhí)行此方法
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("Token Interceptor postHandle");
}
/**
* 不管有沒有異常安疗,這個afterCompletion都會被調(diào)用
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("Token Interceptor afterCompletion");
}
}
配置進spring
/**
* TokenInterceptor 自定義攔截器后,需要配置進Spring
* 也可以mapping够委,跨域設(shè)置
*/
@Slf4j
@Configuration
public class TokenConfig implements WebMvcConfigurer {
@Autowired
TokenInterceptor tokenInterceptor;
/**
* 添加攔截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
log.info("TokenConfig addInterceptors tokenInterceptor");
registry.addInterceptor(tokenInterceptor)
.addPathPatterns("/**")//指定該類攔截的url
.excludePathPatterns( "/static/**");//過濾靜態(tài)資源
}
/**
* 如果實現(xiàn)了Filter跨域攔截荐类,這個跨域無效
* 攔截器實現(xiàn) 跨域支持
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
log.info("TokenConfig addInterceptors addCorsMappings");
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT","OPTIONS","HEAD")
.allowedHeaders("*")
.maxAge(3600);
}
}
第三種方式 : aop攔截
pom.xml 添加Aop支持
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
/**
* pom.xml 添加Aop支持
* <dependency>
* <groupId>org.springframework.boot</groupId>
* <artifactId>spring-boot-starter-aop</artifactId>
* </dependency>
*
* 切片Aspect,既然Spring那么支持AOP茁帽,可以拿到原始的HTTP請求和響應(yīng)的信息玉罐,
* 也可以拿到你真正處理請求方法的信息屈嗤,也可以傳進參數(shù)的那個值。
*
* 攔截順序:filter—>Interceptor-->ControllerAdvice-->@Aspect -->Controller
*/
@Slf4j
@Component //表示它是一個Spring的組件
@Aspect //表示它是一個切面
public class HttpAspect {
/**
* 通過ProceedingJoinPoint對象的getArgs()我們可以得到傳進來的參數(shù)厌小。
* 通過ProceedingJoinPoint對象的proceed()我們可以得到拿到切面方法返回值的對象恢共。
* @param pjp
* @return
* 環(huán)繞通知 首先是:包名 然后是: 類名 然后是方法名:方法名 括號內(nèi)是:參數(shù)
*/
@Around("execution(* com.learn.jwttoken.controller.*.*(..))")
public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable {
log.info("HttpAspect handleControllerMethod filter start");
//原始的HTTP請求和響應(yīng)的信息
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
HttpServletResponse response = attributes.getResponse();
Signature signature = pjp.getSignature();
MethodSignature methodSignature = (MethodSignature)signature;
//獲取當(dāng)前執(zhí)行的方法
Method targetMethod = methodSignature.getMethod();
log.info("當(dāng)前執(zhí)行的方法:{}",targetMethod.getName());
//獲取參數(shù)
Object[] objs = pjp.getArgs();
for (Object obj:objs){
log.info("參數(shù):"+obj);
}
//獲取返回對象
Object object = pjp.proceed();
log.info("獲得返回對象 :{}",object);
log.info("HttpAspect handleControllerMethod filter end");
return pjp.proceed();//代理方法的返回值
}
}