在 Spring中,当请求发送到 Controller 时,在被Controller处理之前,它必须经过 Interceptors(0或多个)。
拦截器的底层原理是AOP
Interceptor 作用
- 日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算 PV(Page View)等;
- 权限检查:如登录检测,进入处理器检测是否登录;
- 性能监控:通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间。(反向代理,如 Apache 也可以自动记录)
HandlerInterceptor接口方法的作用:
- preHandle:处理器执行之前执行,如果返回 false 将跳过处理器、拦截器 postHandle 方法、视图渲染等,直接执行拦截器 afterCompletion 方法。
- postHandle:处理器执行后,视图渲染前执行,如果处理器抛出异常,将跳过该方法直接执行拦截器 afterCompletion 方法。
- afterCompletion:视图渲染后执行,不管处理器是否抛出异常,该方法都将执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| @Component @Slf4j public class JwtTokenUserInterceptor implements HandlerInterceptor {
@Autowired private JwtProperties jwtProperties;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!(handler instanceof HandlerMethod)) { return true; }
String token = request.getHeader(jwtProperties.getUserTokenName()); try { log.info("jwt校验user:{}", token); Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token); Long empId = Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString()); log.info("当前员工id:", empId); BaseContext.setCurrentId(empId); return true; } catch (Exception ex) { response.setStatus(401); return false; }
} @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { BaseContext.removeCurrentId(); } }
|
配置类配置拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @Configuration @Slf4j public class WebMvcConfiguration extends WebMvcConfigurationSupport {
@Autowired private JwtTokenAdminInterceptor jwtTokenAdminInterceptor; @Autowired private JwtTokenUserInterceptor jwtTokenUserInterceptor;
protected void addInterceptors(InterceptorRegistry registry) { log.info("开始注册自定义拦截器..."); registry.addInterceptor(jwtTokenAdminInterceptor) .addPathPatterns("/admin/**") .excludePathPatterns("/admin/employee/login"); registry.addInterceptor(jwtTokenUserInterceptor) .addPathPatterns("/user/**") .excludePathPatterns("/user/user/login"); } }
|
AOP实现拦截器
拦截器的底层其实就是AOP,下面是一个AOP配合注解完成拦截器的功能
定义切面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| @Component("operationAspect") @Aspect public class GlobalOperationAspect {
@Resource private RedisUtils redisUtils;
private static Logger logger = LoggerFactory.getLogger(GlobalOperationAspect.class);
@Before("@annotation(com.easychat.annotation.GlobalInterceptor)") public void interceptorDo(JoinPoint point) { try { Method method = ((MethodSignature) point.getSignature()).getMethod(); GlobalInterceptor interceptor = method.getAnnotation(GlobalInterceptor.class); if (null == interceptor) { return; }
if (interceptor.checkLogin() || interceptor.checkAdmin()) { checkLogin(interceptor.checkAdmin()); } } catch (BusinessException e) { logger.error("全局拦截器异常", e); throw e; } catch (Exception e) { logger.error("全局拦截器异常", e); throw new BusinessException(ResponseCodeEnum.CODE_500); } catch (Throwable e) { logger.error("全局拦截器异常", e); throw new BusinessException(ResponseCodeEnum.CODE_500); } }
private void checkLogin(Boolean checkAdmin) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String token = request.getHeader("token"); TokenUserInfoDto tokenUserInfoDto = (TokenUserInfoDto) redisUtils.get(Constants.REDIS_KEY_WS_TOKEN + token); if (tokenUserInfoDto == null) { throw new BusinessException(ResponseCodeEnum.CODE_901); } if (checkAdmin && !tokenUserInfoDto.getAdmin()) { throw new BusinessException(ResponseCodeEnum.CODE_404); } } }
|
注解代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface GlobalInterceptor {
boolean checkLogin() default true;
boolean checkAdmin() default false; }
|