Java案例怎么配置拦截器?

wen java案例 27

Java案例怎么配置拦截器?从入门到实战的完整指南

目录导读

  1. 什么是拦截器?为什么需要它?

    Java案例怎么配置拦截器?

    • 拦截器的核心作用与适用场景
    • 与过滤器的区别及选择建议
  2. Spring Boot中配置拦截器的3步法

    • 实现HandlerInterceptor接口
    • 注册拦截器到WebMvcConfigurer
    • 指定拦截路径与排除路径
  3. 实战案例:用户登录权限校验

    • 场景分析:未登录用户无法访问受保护资源
    • 代码实现:拦截器逻辑 + 前后端交互示例
  4. 常见问题与解决方案(问答环节)

    • 拦截器不生效怎么办?
    • 如何获取请求体中的参数?
    • 拦截器与AOP的区别?
  5. 性能优化与最佳实践

    • 避免在拦截器中执行耗时操作
    • 使用注解方式代替路径匹配
  6. 总结与扩展

    • 拦截器的其他应用场景(日志记录、国际化等)
    • 推荐学习资源

什么是拦截器?为什么需要它?

拦截器(Interceptor)是Java Web开发中一种对请求进行预处理和后处理的机制,它类似于弹簧,在请求到达Controller之前(preHandle)和Controller处理完成后(postHandle、afterCompletion)插入自定义逻辑。

核心作用

  • 权限校验:检查用户是否登录,是否有访问权限。
  • 日志记录:记录请求参数、响应时间、异常信息。
  • 性能监控:统计接口响应时长。
  • 参数预处理:对请求参数进行解密、校验、格式转换。

与过滤器的区别
| 特性 | 过滤器(Filter) | 拦截器(Interceptor) | |---------------|-------------------------------|-------------------------------| | 作用范围 | 所有Web请求(包括静态资源) | 仅针对Spring MVC的Controller | | 实现方式 | 基于Servlet规范 | 基于Spring AOP思想 | | 获取容器资源 | 较难直接注入Spring Bean | 可直接注入Service、Dao等 | | 典型场景 | 编码设置、XSS过滤 | 权限校验、日志记录 |

建议:跨多个框架的通用处理(如编码)用过滤器,而针对Controller的业务逻辑用拦截器。


Spring Boot中配置拦截器的3步法

第一步:实现HandlerInterceptor接口

创建一个类,重写以下三个方法(最常用的是preHandle):

@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1. 检查用户是否已登录(假设使用Session存储登录状态)
        Object user = request.getSession().getAttribute("user");
        if (user == null) {
            // 未登录,返回错误信息或重定向到登录页
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"code\":401,\"message\":\"请先登录\"}");
            return false; // 拦截请求
        }
        return true; // 放行
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 在Controller执行后、视图渲染前执行(可修改ModelAndView)
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 在视图渲染后执行(一般用于资源清理)
    }
}

关键点

  • return false表示拦截请求,不再执行后续的Controller方法。
  • 如果返回true但响应已提交(比如直接写入了response),后续操作可能失效。

第二步:注册拦截器到WebMvcConfigurer

使用@Configuration类实现WebMvcConfigurer接口,重写addInterceptors方法:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/api/**")          // 拦截所有以/api/开头的路径
                .excludePathPatterns("/api/login")  // 排除登录接口
                .excludePathPatterns("/api/register"); // 排除注册接口
    }
}

路径匹配规则

  • 匹配一级路径,如/user,但不匹配/user/abc
  • 匹配任意层级路径。
  • 支持Ant风格通配符:/api/v1/**匹配/api/v1/xxx

第三步:验证配置是否生效

启动Spring Boot应用,访问受保护的/api/user/info(未登录时),应返回JSON错误信息或重定向到登录页。

常见问题排查

  • 确保LoginInterceptor上有@Component注解。
  • 检查WebConfig类是否被Spring扫描到(如放在启动类同级或子包下)。
  • 若使用Spring Security,需注意拦截器优先级在Security之后。

实战案例:用户登录权限校验

场景描述

假设有一个博客系统,用户必须登录后才能发表评论(/api/comment/add),未登录时返回“请先登录”提示。

代码实现结构

项目结构

src/main/java/com/example/demo
├── config
│   └── WebConfig.java          (注册拦截器)
├── interceptor
│   └── LoginInterceptor.java   (拦截器逻辑)
├── controller
│   └── CommentController.java  (评论接口)
└── DemoApplication.java        (启动类)

拦截器逻辑(LoginInterceptor)

@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 从请求头获取token(实际项目常用JWT)
        String token = request.getHeader("Authorization");
        if (token == null || !"valid_token".equals(token)) { // 简化示例
            response.setStatus(401);
            response.getWriter().write("{\"error\":\"请先登录\"}");
            return false;
        }
        return true;
    }
}

注册拦截器(WebConfig)

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/api/comment/**"); // 仅拦截评论相关接口
    }
}

Controller接口

@RestController
@RequestMapping("/api/comment")
public class CommentController {
    @PostMapping("/add")
    public String addComment(@RequestBody String content) {
        // 只有登录用户才能到达这里
        return "评论成功:" + content;
    }
}

测试结果

  • 调用POST /api/comment/add时未携带token → 返回401 + “请先登录”。
  • 携带正确的token → 返回“评论成功”。

常见问题与解决方案(问答环节)

Q1:拦截器不生效怎么办?

可能原因

  1. 未将拦截器类标记为@Component
  2. WebMvcConfigurer的配置类未被Spring扫描。
  3. 使用了@EnableWebMvc注解(会覆盖Spring Boot的自动配置)。

解决步骤

  • 检查拦截器类是否有@Component
  • 确保配置类在启动类同包或子包下。
  • 如果必须用@EnableWebMvc,需手动配置所有MVC相关组件,不建议一般项目使用。

Q2:如何在拦截器中获取请求体(POST请求的JSON数据)?

注意HttpServletRequestgetInputStream()getReader()只能读取一次,直接读取会导致Controller中无法再获取请求体。

解决方案

  1. 使用Filter + 包装Request:创建一个RequestWrapper,缓存请求体数据。
  2. 在拦截器中通过request.getParameter()获取(仅适用于表单提交)。
  3. 使用Spring的ContentCachingRequestWrapper
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
     ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest) request);
     // 读取一次(会在wrapper中缓存)
     String body = new String(requestWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
     // 后续Controller依然能通过getInputStream读取
     return true;
    }

Q3:拦截器和AOP有什么区别?何时使用拦截器?

维度 拦截器 AOP(面向切面编程)
作用对象 Controller的请求/响应 任意类、方法
访问上下文 可直接获取HttpServletRequest 需通过RequestContextHolder获取
典型用途 登录校验、日志、跨域处理 事务管理、缓存、权限注解

建议

  • 需要操作HttpServletResponse(如重定向、设置响应头)时用拦截器。
  • 需要增强Service层或任意方法时用AOP。

性能优化与最佳实践

1 避免在拦截器中执行耗时操作

问题:拦截器会在每次请求时执行,阻塞后续流程。
优化方案

  • 将数据库查询等耗时操作移到Service层,使用异步处理。
  • 使用Redis缓存权限数据,减少DB访问。

2 使用注解方式代替路径匹配

场景:有些接口需要认证,有些不需要,路径匹配导致配置复杂。
做法:自定义注解@LoginRequired,在拦截器中检查方法上是否有该注解:

@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod method = (HandlerMethod) handler;
            LoginRequired annotation = method.getMethodAnnotation(LoginRequired.class);
            if (annotation != null) {
                // 需要登录的接口,执行校验逻辑
            }
        }
        return true;
    }
}

3 拦截器是单例的

影响:多个请求共享同一个拦截器实例,注意不要在拦截器中定义可变的成员变量(除非使用ThreadLocal)。


总结与扩展

拦截器还能做什么?

  1. 接口防重复提交:在拦截器中检查请求是否在短时间内重复。
  2. 国际化处理:根据请求头中的语言设置响应内容。
  3. 请求/响应加密:在postHandle中对响应体加密,在preHandle中对请求解密。

推荐学习资源

  • 官方文档:Spring Framework Reference Documentation → Web on Servlet Stack → Interceptors
  • 实战项目:GitHub搜索“spring-boot-interceptor-example”
  • 进阶阅读:《Spring实战(第6版)》第8章“处理Web请求”

最后提示:拦截器配置成功后,记得写单元测试验证路径匹配是否正确,使用MockMvc可以模拟请求并检查拦截器行为。


核心心法:拦截器是Spring MVC提供给开发者的一道“网”,你可以决定哪些请求通过,哪些被过滤,配置时牢记“先注册,后排除;注解优先,路径辅助;单例谨慎,异步优秀”,掌握它,你的Java Web应用将更健壮、更安全。

抱歉,评论功能暂时关闭!