4. 鉴权拦截基础:OAuth2.0与JWT原理、Spring Security过滤器链、自定义拦截器设计

在分布式事务与鉴权拦截的单元测试体系中,理解底层鉴权机制是编写可靠拦截逻辑的前提。本章聚焦三大核心:OAuth2.0授权框架、JWT令牌结构、Spring Security过滤器链,并在此基础上推导出自定义拦截器的设计模式。只有掌握这些基础,才能在测试中精准模拟鉴权上下文,避免“假绿”测试。

4.1 OAuth2.0授权框架与JWT令牌

OAuth2.0是一种授权委托协议,允许第三方应用获取对用户资源的有限访问权限,而无需暴露用户密码。在微服务架构中,OAuth2.0通常与JWT(JSON Web Token)结合,作为服务间鉴权的载体。

角色 说明 在分布式系统中的映射
资源所有者 拥有受保护资源的用户 终端用户或上游服务
客户端 请求资源的第三方应用 API Gateway、BFF、内部服务
授权服务器 颁发访问令牌的实体 统一认证中心(UAA)
资源服务器 托管受保护资源的服务 业务微服务(订单、库存等)

JWT令牌结构由三部分组成:Header(头部)、Payload(载荷)、Signature(签名)。Payload中携带用户身份、角色、过期时间等声明,服务端通过验证签名确保令牌未被篡改。

测试要点:在单元测试中,不应依赖真实的授权服务器。应使用预生成的JWT令牌(固定密钥签名)或Mock授权服务器,避免网络调用和外部依赖。
OAuth2.0 授权流程与 JWT 令牌结构 OAuth2.0 授权流程与 JWT 令牌结构 资源所有者 客户端 授权服务器 资源服务器 JWT 令牌结构 Header {"alg":"RS256", "typ":"JWT"} Payload {"sub":"user123", "role":"admin", "exp":1710000000} Signature RSASHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), privateKey)

4.2 Spring Security过滤器链

Spring Security通过过滤器链(Filter Chain)实现安全控制。每个过滤器负责一个安全关注点:认证、授权、会话管理、CSRF保护等。过滤器按顺序执行,任何一个过滤器都可以中断请求。

默认过滤器链中的关键过滤器(按执行顺序):

  1. SecurityContextPersistenceFilter:从Session或请求头中恢复SecurityContext。
  2. UsernamePasswordAuthenticationFilter:处理表单登录认证。
  3. BasicAuthenticationFilter:处理HTTP Basic认证。
  4. BearerTokenAuthenticationFilter:处理Bearer Token(JWT)认证。
  5. FilterSecurityInterceptor:执行授权决策(基于URL或方法注解)。
测试关注点:在单元测试中,通常不启动完整的过滤器链。应使用 @WithMockUser 或手动构造 SecurityContext 来模拟认证状态,避免启动整个Spring容器。
Spring Security 过滤器链(核心过滤器) Spring Security 过滤器链(核心过滤器) SecurityContext PersistenceFilter UsernamePassword AuthenticationFilter BasicAuth Filter BearerToken AuthenticationFilter FilterSecurity Interceptor 过滤器职责说明 • SecurityContextPersistenceFilter:从请求中恢复/保存 SecurityContext • UsernamePasswordAuthenticationFilter:处理表单登录,提取用户名/密码 • BearerTokenAuthenticationFilter:解析 JWT Bearer Token,设置认证信息 • FilterSecurityInterceptor:基于配置的权限规则进行最终授权决策

4.3 自定义拦截器设计模式

在分布式事务场景中,Spring Security过滤器链通常处理用户级鉴权,而业务层面的事务上下文传递(如事务ID、上游服务身份)需要由自定义拦截器完成。设计时应遵循以下原则:

  • 职责单一:每个拦截器只处理一个横切关注点(鉴权、事务传播、日志等)。
  • 顺序可控:通过 @Order 注解或 WebMvcConfigurer 显式指定执行顺序。
  • 可测试性:拦截器逻辑应独立于Spring容器,便于单元测试。

典型自定义拦截器示例(鉴权上下文注入):

public class AuthContextInterceptor implements HandlerInterceptor {
    private final TokenValidator tokenValidator;

    public AuthContextInterceptor(TokenValidator tokenValidator) {
        this.tokenValidator = tokenValidator;
    }

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) {
        String token = extractToken(request);
        if (token == null) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false;
        }
        AuthContext context = tokenValidator.validate(token);
        // 将解析后的上下文注入到当前线程
        AuthContextHolder.set(context);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler, Exception ex) {
        // 清理线程上下文,防止内存泄漏
        AuthContextHolder.clear();
    }

    private String extractToken(HttpServletRequest request) {
        String header = request.getHeader("Authorization");
        if (header != null && header.startsWith("Bearer ")) {
            return header.substring(7);
        }
        return null;
    }
}
常见陷阱:在单元测试中,如果拦截器依赖 HttpServletRequest 或 HttpServletResponse,应使用Mock对象(如Mockito.mock)而非真实Servlet容器。同时注意 AuthContextHolder 的线程安全问题,建议使用 InheritableThreadLocal 或 TransmittableThreadLocal 以支持异步场景。

4.4 鉴权拦截器与过滤器链的协作

在分布式事务鉴权拦截中,Spring Security过滤器链与自定义拦截器形成双层防护:

层次 组件 职责 测试策略
第一层 Spring Security过滤器链 用户认证(JWT验证)、角色授权 使用 @WithMockUser 或 SecurityMockServerConfigurers
第二层 自定义HandlerInterceptor 事务上下文传递、服务间鉴权、请求溯源 Mock请求/响应对象,验证拦截器逻辑

在单元测试中,应分别测试这两层,避免耦合。对于自定义拦截器,推荐使用 MockHttpServletRequest 和 MockHttpServletResponse 进行独立测试,确保拦截逻辑的正确性。

最佳实践:在 @WebMvcTest 中,可以通过 @AutoConfigureMockMvc 结合 MockMvc 测试完整的拦截器链,但应排除外部依赖(如数据库、远程调用)。对于纯逻辑的拦截器,直接使用JUnit + Mockito即可覆盖核心路径。