This is a demo repository for validating the Spring Security configurations via JUnit.
Thanks for the MockMvc object provided by Spring, it is convenient to perform automated testing to validate the configurations of Spring Security with JUnit. The url mapping, parameter extraction, authentication process and the authorization process could be validated. And it is also helpful to develop the api with Test-Driven Development approach.
This repository uses JSON Web Token(JWT) as the validation policy.
./gradlew test
@Configuration
@EnableWebSecurity()
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// ...
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(getUserDetailsService());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/hello/**").authenticated()
.and()
.addFilterBefore(getJwtFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(getLoginFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class);
}
// ...
}
@RestController()
@RequestMapping(value="/hello")
public class HelloController {
@PreAuthorize("hasAuthority('hello.user')")
@GetMapping("/user")
public String user() {
return "Hello User!";
}
@PreAuthorize("hasAuthority('hello.admin')")
@GetMapping("/admin")
public String admin() {
return "Hello Admin!";
}
}
The annotation @WebMvcTest
is required to inject the WebApplicationContext by Spring with @Autowired
annotation. Use MockMvcBuilders.webAppContextSetup
to setup the MockMvc object, and apply the Spring Security:
@WebMvcTest
public class LoginTest {
@Autowired
public WebApplicationContext wac;
public MockMvc mockMvc;
@BeforeEach
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).apply(springSecurity()).build();
}
// ...
}
The two test cases show that one could perform login request and get the token in the response header if the password was correct. However, one would get 401 Unauthorized
if the password was incorrect:
@WebMvcTest
public class LoginTest {
// ...
@Test
public void testLoginSuccess() throws Exception {
mockMvc.perform(post("/login").content("{\"account\":\"user\",\"password\":\"password\"}"))
.andExpect(status().isOk())
.andExpect(header().string("token", matchesPattern(Pattern.compile("^[A-Za-z0-9-_]*\\.[A-Za-z0-9-_]*\\.[A-Za-z0-9-_]*$"))));
}
@Test
public void testLoginFailed_WrongPassword() throws Exception {
mockMvc.perform(post("/login").content("{\"account\":\"user\",\"password\":\"xxxxxxxx\"}"))
.andExpect(status().isUnauthorized());
}
// ...
}
The two test cases show that a user can visit the authenticated url if the request was authorized with the token in the request header. And one will get 403 Forbidden
if the user was without the required authority:
@WebMvcTest
public class HelloTest {
// ...
@Test
public void testUserSuccess() throws Exception {
mockMvc.perform(get("/hello/user").header("Authorization", USER_JWT_TOKEN))
.andExpect(status().isOk());
}
@Test
public void testUserFail_WithoutAuthority() throws Exception {
mockMvc.perform(get("/hello/user").header("Authorization", ADMIN_JWT_TOKEN))
.andExpect(status().isForbidden());
}
// ...
}