1. 简介
在不需要独立集成环境的情况下运行集成测试,是任何软件技术栈中都非常有价值的能力。Spring Boot 与 Spring Security 的无缝集成,使得我们可以轻松地对涉及安全层的组件进行测试。
本文将快速带你了解如何使用 @WebMvcTest
和 @SpringBootTest
来执行带有安全机制的集成测试。
2. 依赖配置
我们首先引入示例所需的依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
其中:
- spring-boot-starter-web 提供 Spring MVC 支持
- spring-boot-starter-security 提供 Spring Security 支持
- spring-boot-starter-test 包含测试相关工具
此外,我们还引入了 spring-security-test,以便使用 @WithMockUser
注解。
3. Web 安全配置
我们的 Web 安全配置相对简单:
✅ 只有认证用户才能访问 /private/**
路径
❌ 所有用户都可以访问 /public/**
路径
@Configuration
public class WebSecurityConfigurer {
@Bean
public InMemoryUserDetailsManager userDetailsService(PasswordEncoder passwordEncoder) {
UserDetails user = User.withUsername("spring")
.password(passwordEncoder.encode("secret"))
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http.authorizeHttpRequests(request -> request.requestMatchers(new AntPathRequestMatcher("/private/**"))
.hasRole("USER"))
.authorizeHttpRequests(request -> request.requestMatchers(new AntPathRequestMatcher("/public/**"))
.permitAll())
.httpBasic(Customizer.withDefaults())
.build();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
4. 方法级安全配置
除了基于 URL 的安全控制外,我们还可以通过方法级别的安全配置来增强保护:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfigurer
extends GlobalMethodSecurityConfiguration {
}
这段配置启用了 Spring Security 的 @PreAuthorize
和 @PostAuthorize
等注解支持。
⚠️ 注意:如果需要更复杂的方法权限控制,可以进一步扩展配置。
更多关于方法级安全的内容,请参考 Spring Security 方法安全详解。
5. 使用 @WebMvcTest
测试 Controller
当使用 @WebMvcTest
注解时,Spring Boot 会自动配置好用于测试的安全过滤器链,因此可以直接使用 @WithMockUser
:
@RunWith(SpringRunner.class)
@WebMvcTest(SecuredController.class)
public class SecuredControllerWebMvcIntegrationTest {
@Autowired
private MockMvc mvc;
// ... other methods
@WithMockUser(value = "spring")
@Test
public void givenAuthRequestOnPrivateService_shouldSucceedWith200() throws Exception {
mvc.perform(get("/private/hello").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
✅ 小贴士:@WebMvcTest
只加载 Web 层,不启动完整上下文,因此速度更快。
6. 使用 @SpringBootTest
测试 Controller
使用 @SpringBootTest
时,我们需要手动配置 MockMvc
的安全过滤器链:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SecuredControllerSpringBootIntegrationTest {
@Autowired
private WebApplicationContext context;
private MockMvc mvc;
@Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
// ... other methods
@WithMockUser("spring")
@Test
public void givenAuthRequestOnPrivateService_shouldSucceedWith200() throws Exception {
mvc.perform(get("/private/hello").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
✅ 推荐做法:使用 springSecurity()
静态方法配置安全过滤器链。
7. 使用 @SpringBootTest
测试受保护的方法
对于方法级别的安全测试,无需额外配置,可直接调用并使用 @WithMockUser
:
@RunWith(SpringRunner.class)
@SpringBootTest
public class SecuredMethodSpringBootIntegrationTest {
@Autowired
private SecuredService service;
@Test(expected = AuthenticationCredentialsNotFoundException.class)
public void givenUnauthenticated_whenCallService_thenThrowsException() {
service.sayHelloSecured();
}
@WithMockUser(username="spring")
@Test
public void givenAuthenticated_whenCallServiceWithSecured_thenOk() {
assertThat(service.sayHelloSecured()).isNotBlank();
}
}
✅ 这种方式适合测试 Service 层的安全逻辑。
8. 使用 @SpringBootTest
和 TestRestTemplate
测试 REST 接口
TestRestTemplate
是测试受保护 REST 接口的一个便捷选择:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SecuredControllerRestTemplateIntegrationTest {
@Autowired
private TestRestTemplate template;
// ... other methods
@Test
public void givenAuthRequestOnPrivateService_shouldSucceedWith200() throws Exception {
ResponseEntity<String> result = template.withBasicAuth("spring", "secret")
.getForEntity("/private/hello", String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
}
}
✅ TestRestTemplate
提供了灵活的认证方式,适用于端到端测试。
了解更多 TestRestTemplate
的用法,请查看 这篇文章。
9. 总结
本文介绍了多种在 Spring Boot 中执行带安全验证的集成测试方式:
- 使用
@WebMvcTest
快速测试 Controller - 使用
@SpringBootTest
+MockMvc
做完整上下文测试 - 直接调用 Service 方法测试方法级安全
- 利用
TestRestTemplate
发起真实 HTTP 请求
所有代码示例均可在 GitHub 获取。