拦截器在工作中依旧还是很常见的,token的认证,权限的认证,部分请求的拦截,都会用到拦截器。前些天公司中需要挂一个拦截的功能上去,突然一下忘了怎么操作的了,各种查资料百度才搞定,这里总结一下。大体上就分为两步,第一步构建一个拦截器,第二步交给springboot进行托管即可。
往简单点说就是构建一个类实现HandlerInteceptor接口。但是其中拦截器实际的执行流程还是需要总结一下的。
首先看HandlerInteceptor中的源码
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.method.HandlerMethod;
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
可以看到其中三个方法,一个是preHandler方法,一个是postHandler方法,另一个是afterCompletion方法,这三个方法的执行流程如下所示:(下图来自《深入浅出spring boot 2.X》一书)
图中流程比较清晰,preHandle方法是在调用处理器正式方法之前启用,如果返回为false,则结束流程,如果返回为true则执行下一步。这三个方法都申明为default因此我们只需要直接实现HandlerInteceptor接口即可。
@Slf4j
public class DBAuthInterceptor implements HandlerInterceptor {
@Autowired
private CommonService commonService;
@Autowired
private DBAuthService dbAuthService;
/**
* 正式进入请求方法之前的拦截操作
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(handler instanceof HandlerMethod){
String accessToken = request.getHeader("accessToken");
if(StringUtils.isBlank(accessToken)){
log.error("--db+token认证缺失token参数");
BaseResponse baseResponse = new BaseResponse(StatusCode.AccessTokenNotExist,"accessToken不存在");
commonService.print(response,baseResponse);
}else{
log.info("-db+token认证开始");
//这里开始解析并验证token
BaseResponse result = dbAuthService.validateToken(accessToken);
if (Objects.equals(result.getStatus(),StatusCode.Success.getCode())){
return true;
}else{
commonService.print(response,result);
return false;
}
}
}
return false;
}
/**
* 执行完目标方法之后的操作
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
if(response.getStatus() == 500){
modelAndView.setViewName("/error/500");
}else if(response.getStatus() == 404){
modelAndView.setViewName("/error/404");
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
这里加一个拦截器,具体业务我们可以不用关系,无非就是完成token的认证罢了。这里只是告知有这个拦截器实现。
交给spring的容器托管有多种方式,一种是直接在springboot启动类上实现WebMvcConfigurer接口,然后在启动类中完成拦截器的托管,不过个人认为这种方式有些耦合。所以这里介绍另一种方式,单独声明一个配置容器类。
@Configuration
public class CustomerWebConfig implements WebMvcConfigurer {
//添加拦截器
/**
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
final String[] interceptorPaths = new String[]{"/dbAuth/token/auth"
, "/dbAuth/token/password/update", "/dbAuth/token/logout"};
registry.addInterceptor(dbAuthInterceptor())
.addPathPatterns(interceptorPaths);
registry.addInterceptor(sessionInterceptor()).addPathPatterns(sessionInterceptor);
}
@Bean
public DBAuthInterceptor dbAuthInterceptor() {
return new DBAuthInterceptor();
}
}
通过@Configuration注解和@Bean注解,这个拦截器才真正交由了spring容器进行托管,才会真正生效。
如果有多个拦截器,会按照添加顺序以责任链的设计模式进行调用和执行,这个就简单提一下。在实际开发中如果采用通配符的方式配置拦截路径,如果多个拦截器之间因为通配符的方式有交叉的拦截部分,建议在拦截器内部进一步判断url。
偏简单的使用,没啥可总结的,参考了《深入浅出springboot 2.X》一书