1. SpringMVC拦截器核心机制解析
SpringMVC拦截器是基于Java反射和动态代理实现的AOP实践,其本质是通过HandlerInterceptor接口在控制器方法执行前后插入横切逻辑。与Servlet Filter不同,拦截器工作在Spring上下文层面,能够直接获取IoC容器中的Bean实例。
1.1 拦截器三大生命周期方法
public interface HandlerInterceptor { // 控制器方法执行前触发(预处理) default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {} // 控制器方法执行后触发(后处理,视图渲染前) default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {} // 整个请求完成后触发(资源清理) default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {} }预处理阶段(preHandle)的返回值决定是否继续执行后续拦截器和控制器方法。返回false时会中断执行链,常用于权限校验失败时的拦截。
2. 登录验证拦截器实现细节
2.1 会话状态检查方案对比
| 方案类型 | 实现方式 | 优点 | 缺点 |
|---|---|---|---|
| Session校验 | request.getSession() | 服务端状态可靠 | 集群环境需要会话共享 |
| Token验证 | 解析Authorization头 | 无状态、适合分布式 | 需要处理Token续期 |
| Cookie检测 | 读取加密的Cookie值 | 实现简单 | 安全性较低 |
推荐采用JWT+Session的混合方案:用Session存储敏感权限信息,用JWT实现无状态校验。
2.2 完整登录拦截器实现
public class AuthInterceptor implements HandlerInterceptor { private final List<String> excludePaths = Arrays.asList( "/login", "/register", "/static/**"); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String uri = request.getRequestURI(); // 放行白名单路径 if(excludePaths.stream().anyMatch(uri::startsWith)){ return true; } HttpSession session = request.getSession(false); if(session == null || session.getAttribute("user") == null) { response.sendRedirect("/login?redirect=" + URLEncoder.encode(uri, "UTF-8")); return false; } return true; } }关键细节:session.getSession(false)参数避免自动创建新会话,防止内存泄漏
3. 拦截器执行流程深度剖析
3.1 典型执行时序图
- 用户请求
/admin/dashboard - DispatcherServlet确定HandlerMapping
- 执行拦截器链preHandle()方法
- 按注册顺序正向执行
- 任一返回false则终止流程
- 实际控制器方法执行
- 执行拦截器链postHandle()方法
- 按注册顺序逆向执行
- 视图渲染
- 执行afterCompletion()方法
- 按注册顺序逆向执行
- 即使控制器抛出异常也会执行
3.2 多拦截器协作示例
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LogInterceptor()) .order(1) .addPathPatterns("/**"); registry.addInterceptor(new AuthInterceptor()) .order(2) .addPathPatterns("/admin/**"); } }执行顺序:
- LogInterceptor#preHandle
- AuthInterceptor#preHandle
- Controller方法
- AuthInterceptor#postHandle
- LogInterceptor#postHandle
- AuthInterceptor#afterCompletion
- LogInterceptor#afterCompletion
4. 生产环境实战经验
4.1 性能优化要点
- 在preHandle中进行轻量级校验(如登录态)
- 将耗时操作(权限树加载)移到postHandle
- 使用@Order控制拦截器顺序,安全类拦截器应靠后
- 对静态资源配置excludePathPatterns
4.2 常见问题排查
问题1:拦截器不生效
- 检查是否被@ComponentScan扫描到
- 确认路径模式是否匹配(/**会匹配所有路径)
- 查看是否有更高优先级的拦截器返回了false
问题2:循环重定向
// 错误示例:对登录页也进行拦截 public boolean preHandle(...) { if(未登录){ response.sendRedirect("/login"); // 导致死循环 } }解决方案:务必配置excludePaths排除登录相关URL
问题3:跨域拦截冲突当同时使用CORS Filter和拦截器时,建议:
- 在Filter中设置基础CORS头
- 在拦截器preHandle中处理复杂跨域逻辑
- 确保OPTIONS请求不被拦截
5. 进阶开发技巧
5.1 注解式权限控制
结合自定义注解实现更灵活的权限管理:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RequireRole { String[] value(); } public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(...) { HandlerMethod handlerMethod = (HandlerMethod)handler; RequireRole annotation = handlerMethod.getMethodAnnotation(RequireRole.class); if(annotation != null) { // 角色校验逻辑 } } }5.2 异步请求处理
对于Async请求,需要额外配置AsyncHandlerInterceptor:
public class AsyncAuthInterceptor extends HandlerInterceptorAdapter { @Override public void afterConcurrentHandlingStarted(...) { // 异步请求开始时触发 // 可在此处清理ThreadLocal变量 } }实际项目中,建议结合Spring Security进行更完善的安全控制,拦截器适合处理业务层面的轻量级校验逻辑。对于需要精细权限控制的场景,推荐使用@PreAuthorize注解方案。