如何对使用OAuth2的REST Controller进行Mock测试
我之前在测试用OAuth2客户端模式保护的REST接口时也卡过一阵,后来发现Spring生态里有非常成熟的Mock方案,完全不用对接真实的授权服务器,下面给你详细说说怎么弄:
核心思路
OAuth2客户端模式的本质是客户端身份认证,而非用户认证。所以测试时我们不需要模拟用户,只需要模拟一个已通过认证的客户端身份,让Spring Security认为这个请求是来自合法客户端的即可。Spring Security Test提供了专门的工具来做这件事。
步骤1:添加测试依赖
首先确保你的项目里引入了Spring Security Test依赖(如果用Maven的话):
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency>
步骤2:两种Mock认证的实现方式
方式一:用@WithMockOAuth2Client快速实现(简单场景)
Spring Security 5.2+提供了@WithMockOAuth2Client注解,它会自动帮你模拟一个已认证的OAuth2客户端,直接加在测试类或测试方法上就行:
@WebMvcTest(YourProtectedController.class) @WithMockOAuth2Client class YourProtectedControllerTest { @Autowired private MockMvc mockMvc; @Test void testProtectedEndpoint() throws Exception { mockMvc.perform(get("/api/protected/resource")) .andExpect(status().isOk()) .andExpect(content().json("{\"message\":\"Hello from protected endpoint\"}")); } }
这个注解默认会使用一个名为test的客户端ID,如果你需要自定义客户端ID,可以用@WithMockOAuth2Client(clientId = "your-custom-client-id")。
方式二:手动构建认证(灵活场景)
如果你的接口有客户端权限/Scope校验(比如用@PreAuthorize("#oauth2.hasScope('read')")),或者需要自定义更多客户端属性,那就手动构建OAuth2AuthenticationToken:
@WebMvcTest(YourProtectedController.class) class YourProtectedControllerTest { @Autowired private MockMvc mockMvc; @Test void testClientWithSpecificScope() throws Exception { // 1. 构建客户端注册信息 ClientRegistration clientRegistration = ClientRegistration.withRegistrationId("custom-client") .clientId("my-app-client") .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) .tokenUri("https://mock-auth-server/token") .build(); // 2. 构建带有Scope的认证Token OAuth2AuthenticationToken authToken = new OAuth2AuthenticationToken( new OAuth2ClientPrincipal(clientRegistration.getClientId()), List.of(new SimpleGrantedAuthority("SCOPE_read")), // 这里设置客户端的Scope/权限 clientRegistration.getRegistrationId() ); // 3. 用MockMvc执行请求时注入认证信息 mockMvc.perform(get("/api/protected/resource") .with(SecurityMockMvcRequestPostProcessors.authentication(authToken))) .andExpect(status().isOk()); } }
这种方式可以完全自定义客户端的身份信息、权限,完美匹配你的真实业务场景。
步骤3:处理权限校验
如果你的Controller方法用了基于客户端的权限控制,比如:
@GetMapping("/api/admin") @PreAuthorize("#oauth2.clientHasRole('ADMIN')") public String adminEndpoint() { return "Admin content"; }
那在手动构建认证时,需要把对应的角色权限加进去:
List.of(new SimpleGrantedAuthority("ROLE_ADMIN"))
这样测试时就能通过权限校验。
避坑提示
- 一定要用
@WebMvcTest来写Controller测试,它会只加载Controller和Spring Security相关的Bean,测试速度更快,也避免了不必要的上下文加载。 - 不要直接在请求头里加假的Bearer Token然后关闭认证校验——这种方式测不到真实的认证逻辑,等于白测。用Spring Security Test的Mock认证才是正确的方式,它会完全模拟真实的认证流程,只是跳过了和授权服务器的交互。
内容的提问来源于stack exchange,提问作者Phil Palmiero

