Spring Boot 拦截器实战:登录验证、日志记录、权限控制一网打尽!

💡 前言

在 Web 开发中,我们经常需要对请求进行统一处理,比如:

  • 判断用户是否已登录
  • 记录请求日志
  • 实现接口权限校验
  • 防止重复提交
  • 统一设置响应头信息

这些功能如果都写在每个 Controller 里,不仅代码冗余,还难以维护。而 Spring Boot 提供了强大的拦截器(Interceptor)机制,让我们可以像 AOP 一样,在请求到达 Controller 之前或之后进行统一处理。

本文将带你从零开始掌握 Spring Boot 拦截器的使用,并通过多个实用案例展示其强大之处!


🧪 一、什么是 Spring Boot 拦截器?

✅ 定义:

拦截器(HandlerInterceptor)是 Spring MVC 提供的一种用于拦截 HTTP 请求的机制。它可以在请求被 Controller 处理前后执行特定逻辑。

✅ 与 Filter 的区别:

对比项 Interceptor(拦截器) Filter(过滤器)
所属框架 Spring MVC Servlet 标准
精确度 可以精确到 Controller 方法级别 作用于所有请求
支持 Spring 注入 ✅ 支持 ❌ 不支持
执行时机 preHandle -> Controller -> postHandle -> afterCompletion doFilter

📌 适用场景:

  • 登录状态校验
  • 接口权限控制
  • 请求日志记录
  • 统一返回格式封装
  • 防止重复提交

🛠️ 二、自定义拦截器实现步骤

Step 1:创建拦截器类

实现 HandlerInterceptor 接口,并重写以下三个方法:

java 复制代码
public class LoginInterceptor implements HandlerInterceptor {

    // 请求进入 Controller 之前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("Authorization");
        if (token == null || !token.equals("abc123")) {
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write("{\"code\":401,\"message\":\"未登录\"}");
            return false; // 中断请求
        }
        return true;
    }

    // Controller 方法执行完成后执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 可选操作,如添加响应头
        response.setHeader("X-Custom-Header", "interceptor-added");
    }

    // 整个请求完成(包括视图渲染)后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 可用于资源清理、日志记录等
        System.out.println("【拦截器】请求结束:" + request.getRequestURI());
    }
}

Step 2:注册拦截器

创建配置类,继承 WebMvcConfigurer 并重写 addInterceptors() 方法:

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")         // 拦截所有路径
                .excludePathPatterns("/login", "/error"); // 排除不需要拦截的路径
    }
}

🎯 三、拦截器典型应用场景实战

✅ 场景一:登录鉴权(Token 校验)

拦截所有非登录请求,判断请求头中是否携带有效 Token。

java 复制代码
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    String token = request.getHeader("Authorization");
    if (token == null || !isValidToken(token)) {
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        return false;
    }
    return true;
}

✅ 场景二:请求日志记录

记录每次请求的 URL、IP、耗时、参数等信息。

java 复制代码
private static final ThreadLocal<Long> startTimeThreadLocal = new ThreadLocal<>();

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    startTimeThreadLocal.set(System.currentTimeMillis());
    System.out.println("【请求开始】IP: " + request.getRemoteAddr() + ", URI: " + request.getRequestURI());
    return true;
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    long duration = System.currentTimeMillis() - startTimeThreadLocal.get();
    System.out.println("【请求结束】耗时: " + duration + "ms");
    startTimeThreadLocal.remove();
}

✅ 场景三:接口权限控制(基于角色)

根据当前用户的角色判断是否有权限访问某个接口。

java 复制代码
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    User user = getCurrentUser(request);
    if (!user.hasPermission(request.getRequestURI())) {
        response.getWriter().write("{\"code\":403,\"message\":\"无权限\"}");
        return false;
    }
    return true;
}

✅ 场景四:防止重复提交

通过 Token 或 Redis 缓存来识别重复请求。

java 复制代码
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    String token = request.getParameter("formToken");
    if (StringUtils.isEmpty(token) || RedisUtil.exists(token)) {
        response.getWriter().write("{\"code\":400,\"message\":\"请勿重复提交\"}");
        return false;
    }
    RedisUtil.set(token, "submitted", 60); // 设置有效期
    return true;
}

🧭 四、拦截器使用注意事项

注意事项 说明
拦截器顺序问题 多个拦截器按照注册顺序依次执行 preHandle,逆序执行 afterCompletion
不要阻塞主线程 如需异步处理,请使用线程池
避免频繁调用耗时操作 如数据库查询、远程调用等,可能影响性能
使用 ThreadLocal 要注意内存泄漏 每次请求结束后记得 remove 清理
不建议替代 Filter 如跨域、编码处理等仍推荐使用 Filter

📘 五、总结对比表

功能 说明
preHandle 在 Controller 执行前调用,可中断请求
postHandle 在 Controller 执行后、视图渲染前调用
afterCompletion 请求完全结束后调用,可用于资源释放
应用场景 登录校验、权限控制、日志记录、防重提交
优点 解耦业务逻辑、统一处理请求
缺点 滥用会影响性能、增加系统复杂度

🎁 六、结语

Spring Boot 拦截器是构建高质量 Web 应用不可或缺的利器之一。通过合理使用拦截器,我们可以优雅地实现登录验证、权限控制、日志记录等通用功能,使代码更简洁、系统更健壮。

无论你是开发企业级后台管理系统,还是搭建高并发 API 接口平台,都应该掌握拦截器的使用技巧。


🎯 点赞、收藏、转发本文,让更多开发者受益!

相关推荐
青莳吖4 小时前
使用 SseEmitter 实现 Spring Boot 后端的流式传输和前端的数据接收
前端·spring boot·后端
椰椰椰耶6 小时前
[网页五子棋][匹配模块]实现胜负判定,处理玩家掉线
java·开发语言·spring boot·websocket·spring
噼里啪啦啦.7 小时前
SpringBoot统一功能处理
java·spring boot·后端
考虑考虑7 小时前
JPA自定义sql参数为空和postgresql遇到问题
spring boot·后端·spring
向上的车轮7 小时前
Spring Boot微服务架构(十一):独立部署是否抛弃了架构优势?
spring boot·微服务·架构
向上的车轮9 小时前
Spring Boot微服务架构(十):Docker与K8S部署的区别
spring boot·微服务·架构
紫气东来,茉上花开9 小时前
[特殊字符] Spring Boot底层原理深度解析与高级面试题精析
spring boot·后端·spring
一只爱撸猫的程序猿9 小时前
构建一个简单智能客户服务系统的案例
spring boot·程序员·mcp
ghie90909 小时前
Spring Boot使用Redis实现分布式锁
spring boot·redis·分布式
秋野酱9 小时前
基于springboot的医护人员排班系统设计与实现(源码+文档+部署讲解)
java·spring boot·后端