You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring MVC WebClient客户端凭证流OAuth2集成测试编写方法

Spring MVC OAuth2客户端凭证流场景集成测试实现方案

测试核心覆盖三个验证目标:

  • 客户端凭证流配置正确性:可按配置参数从授权服务器获取带指定scope的有效JWT
  • 接口权限控制逻辑:无令牌、令牌缺少必要scope时返回对应鉴权失败状态码,携带合法scope令牌可通过鉴权
  • 业务功能正确性:合法请求下接口返回符合预期的业务响应

前置依赖准备

测试采用「本地启动服务+WireMock模拟授权服务器」的方式,不依赖真实外部授权服务,保证测试稳定性,test作用域引入以下依赖:

  • spring-boot-starter-test:提供Spring Boot测试核心能力、随机端口启动、测试上下文加载支持
  • spring-security-test:提供JWT模拟、安全上下文测试工具
  • WireMock:用于模拟授权服务器的令牌端点、JWK端点,支持请求校验、自定义响应

测试环境配置

在测试资源目录下创建application-test.yaml,将授权服务器地址替换为WireMock的本地监听地址,其余客户端配置(客户端ID、密钥、授权模式、scope)与生产配置保持一致,示例配置:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: http://localhost:${wiremock.server.port}/oauth2/jwks
      client:
        provider:
          mock-auth:
            token-uri: http://localhost:${wiremock.server.port}/oauth2/token
        registration:
          my-service:
            provider: mock-auth
            client-id: test-client
            client-secret: test-secret
            authorization-grant-type: client_credentials
            scope: message.send
wiremock:
  server:
    port: 0 # 随机端口启动,避免端口冲突

分场景测试实现

测试类基础配置如下,采用随机端口启动完整Spring上下文,激活test配置文件,自动启动WireMock:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
@AutoConfigureWireMock
class MessageApiIntegrationTest {

    @Autowired
    private AuthClientManager authClientManager;
    @LocalServerPort
    private int servicePort;
    @Autowired
    private ObjectMapper objectMapper;

    private WebClient testWebClient;

    @BeforeEach
    void setUp() {
        // 初始化指向本地测试服务的基础WebClient
        testWebClient = WebClient.builder()
                .baseUrl("http://localhost:" + servicePort)
                .build();
        // 重置WireMock桩,避免用例间互相影响
        WireMock.reset();
    }
}

1. 客户端凭证流配置正确性验证

首先提前生成一对RSA密钥对,用于模拟JWT签发与校验:

  • 用私钥签名生成测试JWT,根据不同用例设置不同scope、过期时间等字段
  • 用公钥构造JWK响应,模拟授权服务器的JWK端点
  • 配置WireMock令牌端点桩:校验请求必须携带grant_type=client_credentials、scope=message.send参数,且请求头携带正确的客户端Basic认证(客户端ID:密钥的Base64编码),校验通过则返回签名后的有效JWT,否则返回401

测试逻辑:

  • 调用authClientManager获取my-service客户端对应的携带OAuth凭证的WebClient
  • 发起测试请求,校验WireMock收到的请求头中是否携带格式正确的Authorization: Bearer <token>
  • 解析请求中的JWT,校验签发主体、scope、过期时间是否符合配置预期

2. 接口权限控制逻辑验证

这部分可以结合Spring Security Test提供的注解快速构造安全上下文,不需要每次走完整的令牌获取流程,提升测试执行效率:

  • 无令牌请求用例:直接用基础testWebClient调用/v1/message接口,断言返回401状态码
  • 权限不足用例:用@WithMockJwt(authorities = "SCOPE_other.scope")注解标记测试方法,构造带其他scope的JWT上下文,调用接口断言返回403状态码
  • 权限通过用例:用@WithMockJwt(authorities = "SCOPE_message.send")注解标记测试方法,构造带正确scope的JWT上下文,调用接口断言不会被权限拦截,可进入业务逻辑

示例代码:

@Test
void whenRequestWithoutToken_thenReturn401() {
    testWebClient.post()
            .uri("/v1/message")
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(new SendMessageRequest("test@example.com", "test content"))
            .exchange()
            .expectStatus().isUnauthorized();
}

@Test
@WithMockJwt(authorities = "SCOPE_wrong.scope")
void whenRequestWithoutRequiredScope_thenReturn403() {
    testWebClient.post()
            .uri("/v1/message")
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(new SendMessageRequest("test@example.com", "test content"))
            .exchange()
            .expectStatus().isForbidden();
}

3. 端到端业务功能验证

这个用例走完整真实流程:

  • 配置WireMock的JWK端点返回公钥、令牌端点返回带message.send scope的有效JWT
  • 通过authClientManager获取my-service对应的、自动携带OAuth凭证的WebClient
  • 构造合法的邮件发送请求参数,调用本地服务的/v1/message接口
  • 断言返回200状态码,且返回体业务字段符合预期,同时校验服务内部业务逻辑(比如邮件记录落库、下游调用触发等)是否正常执行

注意事项

  • 不要在集成测试中连接真实授权服务器,否则测试稳定性会受外部服务可用性、网络环境影响,CI环境执行失败率极高
  • Spring Security对OAuth2 scope的默认权限前缀是SCOPE_,测试构造权限、写权限校验表达式时注意前缀匹配,hasScope('message.send')和hasAuthority('SCOPE_message.send')是等价写法
  • 模拟JWT时注意exp过期时间要设置为当前时间之后,否则会被令牌校验逻辑判定为无效令牌
  • 每个用例执行后要重置WireMock桩,避免上一个用例的模拟配置影响后续用例的断言结果

内容的提问来源于stack exchange,提问作者skyho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 21:09:16