middle
Как тестировать Spring Security?
Spring Security предоставляет модуль spring-security-test с инструментами для тестирования аутентификации и авторизации.
Зависимость
Пример
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
WithMockUser — эмуляция аутентифицированного пользователя
Пример
@WebMvcTest(UserController.class)
class SecuredControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
@WithMockUser(username = "admin", roles = {"ADMIN"})
void adminShouldAccessAdminEndpoint() throws Exception {
mockMvc.perform(get("/api/admin/users"))
.andExpect(status().isOk());
}
@Test
@WithMockUser(username = "user", roles = {"USER"})
void regularUserShouldNotAccessAdminEndpoint() throws Exception {
mockMvc.perform(get("/api/admin/users"))
.andExpect(status().isForbidden());
}
@Test
void anonymousUserShouldGetUnauthorized() throws Exception {
mockMvc.perform(get("/api/admin/users"))
.andExpect(status().isUnauthorized());
}
}
SecurityMockMvcRequestPostProcessors — настройка безопасности в запросе
Пример
import static org.springframework.security.test.web.servlet.request
.SecurityMockMvcRequestPostProcessors.*;
@Test
void shouldAuthenticateWithCsrf() throws Exception {
mockMvc.perform(post("/api/users")
.with(csrf())
.with(user("admin").roles("ADMIN"))
.contentType(MediaType.APPLICATION_JSON)
.content("{\"name\": \"Иван\"}"))
.andExpect(status().isCreated());
}
@Test
void shouldAuthenticateWithHttpBasic() throws Exception {
mockMvc.perform(get("/api/users")
.with(httpBasic("user", "password")))
.andExpect(status().isOk());
}
Кастомная аннотация для повторяющихся сценариев
Пример
@Retention(RetentionPolicy.RUNTIME)
@WithMockUser(username = "admin", roles = {"ADMIN"})
public @interface WithAdmin {}
@Retention(RetentionPolicy.RUNTIME)
@WithMockUser(username = "user", roles = {"USER"})
public @interface WithRegularUser {}
// Использование
@Test
@WithAdmin
void shouldAccessAsAdmin() throws Exception {
mockMvc.perform(get("/api/admin/dashboard"))
.andExpect(status().isOk());
}
Интеграционный тест с TestRestTemplate
Пример
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class SecurityIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
void shouldReturnUnauthorizedWithoutCredentials() {
ResponseEntity<String> response =
restTemplate.getForEntity("/api/users", String.class);
assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
}
@Test
void shouldReturnOkWithValidCredentials() {
ResponseEntity<String> response = restTemplate
.withBasicAuth("admin", "password")
.getForEntity("/api/users", String.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
}
На собеседовании: минимум, который нужно знать —
@WithMockUserиcsrf(). Частая ошибка — не добавить.with(csrf())при тестировании POST/PUT/DELETE-запросов, из-за чего тест падает с 403 Forbidden.