1. 簡介
執行集成測試無需獨立的集成環境的能力是任何軟件棧中一項有價值的功能。 Spring Boot 與 Spring Security 的無縫集成使得測試與安全層交互的組件變得簡單。
在本快速教程中,我們將探索使用 @MockMvcTest 和 @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>
The spring-boot-starter-web,spring-boot-starter-security, and spring-boot-starter-test 這些啓動器為我們提供了對 Spring MVC、Spring Security 和 Spring Boot 測試實用程序訪問權限。
此外,我們還會引入 spring-security-test 以便獲取我們即將使用的 @WithMockUser 註解的訪問權限。
3. Web Security 配置
我們的 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. 方法安全配置
除了我們在 WebSecurityConfigurer中定義的基於 URL 路徑的安全策略,我們還可以通過提供額外的配置文件來配置基於方法的安全策略:@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfigurer
extends GlobalMethodSecurityConfiguration {
}
此配置啓用對 Spring Security 的 pre/post 註解的支持。 如果需要額外的支持,其他屬性也可用。 要了解有關 Spring 方法安全的信息,請查看有關此主題的文章。
5. 使用 測試控制器
使用 Spring Security 中的 註解方法時,MockMvc 會自動配置所需的過濾器鏈,用於測試我們的安全配置。
由於 MockMvc 已經配置好,因此我們可以在測試中使用 註解,而無需進行任何額外的配置:
@RunWith(SpringRunner.class)
@WebMvcTest(SecuredController.class)
public class SecuredControllerWebMvcIntegrationTest {
@Autowired
private MockMvc mvc;
// ... 其他方法
@WithMockUser(value = "spring")
@Test
public void givenAuthRequestOnPrivateService_shouldSucceedWith200() throws Exception {
mvc.perform(get("/private/hello").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
請注意,使用 會告訴 Spring Boot 只實例化 Web 層,而不會實例化整個上下文。因此,使用 的控制器測試比其他方法運行速度更快。
6. 使用<em>@SpringBootTest</em>測試控制器
在使用<em>@SpringBootTest</em>註解測試帶有 Spring Security 的控制器時,設置<em>MockMvc</em>時,必須明確配置過濾器鏈。使用<em>SecurityMockMvcConfigurer</em>提供的靜態<em>springSecurity</em>方法是實現這一目的的首選方法:
@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());
}
}
7. 使用 測試安全方法
不需要任何額外的配置來測試安全方法。我們可以直接調用方法並根據需要使用:
@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();
}
}
8. 使用 @SpringBootTest 和 TestRestTemplate 進行測試
TestRestTemplate 是編寫對安全 REST 端點進行集成測試的便捷選項。
我們可以自動注入一個模板並設置憑據,然後再請求受保護的端點:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SecuredControllerRestTemplateIntegrationTest {
@Autowired
private TestRestTemplate template;
// ... 其他方法
@Test
public void givenAuthRequestOnPrivateService_shouldSucceedWith200() throws Exception {
ResponseEntity result = template.withBasicAuth("spring", "secret")
.getForEntity("/private/hello", String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
}
}
TestRestTemplate 靈活且提供許多有用的安全相關選項。有關 TestRestTemplate 的更多詳細信息,請查看有關該主題的文章。
9. 結論
在本文中,我們探討了多種執行安全集成測試的方法。
我們研究瞭如何與 MVC 控制器和 REST 端點以及安全方法一起工作。