Spring Boot:如何测试带自定义PreAuthorize表达式与认证的REST API
测试带有自定义权限校验的Spring Boot REST API方案
针对你这个结合了Spring Security自定义权限校验的Spring Boot REST API,我整理了一套实用的测试方案,覆盖核心场景,同时保持测试的独立性:
核心思路
咱们需要:
- 聚焦测试Controller层的权限逻辑和接口响应
- Mock掉依赖的业务服务(
SomeService)和用户权限对象(LoggedUser),避免依赖真实系统 - 模拟Spring Security的认证上下文,分别测试已授权、未授权、未登录的场景
提前准备:测试依赖
确保你的pom.xml(或build.gradle)里包含这些测试依赖:
<!-- Spring Boot 基础测试依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- Spring Security 测试支持 --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency>
具体测试代码实现
1. 测试类基础配置
用@WebMvcTest来只加载Controller相关的上下文,轻量高效;同时用@MockBean替换真实的依赖为Mock对象:
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @WebMvcTest(SomeRestController.class) public class SomeRestControllerTest { @Autowired private MockMvc mockMvc; // 用来模拟HTTP请求 @MockBean private SomeService someService; // Mock业务服务,隔离真实业务逻辑 @MockBean private LoggedUser loggedUser; // Mock当前用户对象,控制权限返回 }
2. 测试「有权限用户正常访问」场景
当用户权限允许时,接口应该正常返回业务数据:
@Test @WithMockUser // 快速模拟一个已登录的认证用户 public void getSomeObject_WithValidPermission_ReturnsOk() throws Exception { // 1. 模拟用户权限:允许访问 Permissions mockPerms = new Permissions(); mockPerms.setCanGetSome(true); when(loggedUser.getPermissions()).thenReturn(mockPerms); // 2. 模拟业务服务返回的数据 SomeObject mockResponse = new SomeObject(); mockResponse.setId(1); mockResponse.setContent("Test Content"); when(someService.getSomeObject()).thenReturn(mockResponse); // 3. 发起请求并验证结果 mockMvc.perform(get("/getSome")) .andExpect(status().isOk()) .andExpect(jsonPath("$.id").value(1)) .andExpect(jsonPath("$.content").value("Test Content")); }
3. 测试「无权限用户访问被拒绝」场景
当权限不允许时,接口应该返回403 Forbidden:
@Test @WithMockUser public void getSomeObject_WithInvalidPermission_ReturnsForbidden() throws Exception { // 模拟用户权限:不允许访问 Permissions mockPerms = new Permissions(); mockPerms.setCanGetSome(false); when(loggedUser.getPermissions()).thenReturn(mockPerms); // 验证请求被拒绝 mockMvc.perform(get("/getSome")) .andExpect(status().isForbidden()); }
4. 测试「未登录用户访问」场景
如果你的接口要求必须登录,未登录用户访问应该返回401 Unauthorized:
@Test public void getSomeObject_WithoutLogin_ReturnsUnauthorized() throws Exception { mockMvc.perform(get("/getSome")) .andExpect(status().isUnauthorized()); }
关键细节说明
@WebMvcTestvs@SpringBootTest:前者只加载Controller和相关配置,测试速度更快,适合单元测试;后者加载全量上下文,适合集成测试,这里用前者就足够了。- 不用Mock
CanGetSome:这个组件是我们要测试的权限逻辑一部分,Spring会自动注入它,我们只需要Mock它依赖的LoggedUser,就能真实触发它的isValid()校验逻辑,确保测试的准确性。 @WithMockUser的灵活用法:如果需要模拟特定用户名、角色,可以用@WithMockUser(username = "testUser", roles = {"USER"}),满足更复杂的认证场景。
内容的提问来源于stack exchange,提问作者Kamil W
相关产品推荐
相关产品推荐

