Streamlining for the answer I could not find one to be simple and flexible at the same time, then I found Spring Security Link and I realized that there are almost perfect solutions. AOP solutions are often the largest for testing, and Spring provides it with @WithMockUser , @WithUserDetails and @WithSecurityContext in this artifact:
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <version>4.2.2.RELEASE</version> <scope>test</scope> </dependency>
In most cases, @WithUserDetails gathers the flexibility and power I need.
How does @WithUserDetails work?
Basically you just need to create a custom UserDetailsService with all the possible user profiles that you want to test. for example
@TestConfiguration public class SpringSecurityWebAuxTestConfig { @Bean @Primary public UserDetailsService userDetailsService() { User basicUser = new UserImpl("Basic User", "user@company.com", "password"); UserActive basicActiveUser = new UserActive(basicUser, Arrays.asList( new SimpleGrantedAuthority("ROLE_USER"), new SimpleGrantedAuthority("PERM_FOO_READ") )); User managerUser = new UserImpl("Manager User", "manager@company.com", "password"); UserActive managerActiveUser = new UserActive(managerUser, Arrays.asList( new SimpleGrantedAuthority("ROLE_MANAGER"), new SimpleGrantedAuthority("PERM_FOO_READ"), new SimpleGrantedAuthority("PERM_FOO_WRITE"), new SimpleGrantedAuthority("PERM_FOO_MANAGE") )); return new InMemoryUserDetailsManager(Arrays.asList( basicActiveUser, managerActiveUser )); } }
Now we have our users, so imagine that we want to test access control for this controller function:
@RestController @RequestMapping("/foo") public class FooController { @Secured("ROLE_MANAGER") @GetMapping("/salute") public String saluteYourManager(@AuthenticationPrincipal User activeUser) { return String.format("Hi %s. Foo salutes you!", activeUser.getUsername()); } }
Here we have the mapped route function / foo / salute , and we are testing role-based security with the @Secured annotation, although you can test @PreAuthorize and @PostAuthorize as well. Let's create two tests: one to check if a valid user can see this salute response, and the other to check if it is really forbidden.
@RunWith(SpringRunner.class) @SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SpringSecurityWebAuxTestConfig.class ) @AutoConfigureMockMvc public class WebApplicationSecurityTest { @Autowired private MockMvc mockMvc; @Test @WithUserDetails("manager@company.com") public void givenManagerUser_whenGetFooSalute_thenOk() throws Exception { mockMvc.perform(MockMvcRequestBuilders.get("/foo/salute") .accept(MediaType.ALL)) .andExpect(status().isOk()) .andExpect(content().string(containsString("manager@company.com"))); } @Test @WithUserDetails("user@company.com") public void givenBasicUser_whenGetFooSalute_thenForbidden() throws Exception { mockMvc.perform(MockMvcRequestBuilders.get("/foo/salute") .accept(MediaType.ALL)) .andExpect(status().isForbidden()); } }
As you can see, we have imported SpringSecurityWebAuxTestConfig to provide testing for our users. Each of them was used in the corresponding test case, simply using simple annotation, reducing code and complexity.
Better use @WithMockUser for easier role-based protection
As you can see, @WithUserDetails has all the flexibility needed for most of your applications. It allows users to be used with any GrantedAuthority, such as roles or permissions. But if you just work with roles, testing can be even easier and you can avoid creating a custom UserDetailsService . In such cases, specify a simple combination of user, password and roles using @WithMockUser .
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented @WithSecurityContext( factory = WithMockUserSecurityContextFactory.class ) public @interface WithMockUser { String value() default "user"; String username() default ""; String[] roles() default {"USER"}; String password() default "password"; }
Annotations define default values for a very simple user. As in our case, the route we are testing requires the authenticated user to be a manager, we can refuse to use SpringSecurityWebAuxTestConfig and do this.
@Test @WithMockUser(roles = "MANAGER") public void givenManagerUser_whenGetFooSalute_thenOk() throws Exception { mockMvc.perform(MockMvcRequestBuilders.get("/foo/salute") .accept(MediaType.ALL)) .andExpect(status().isOk()) .andExpect(content().string(containsString("user"))); }
Note that now instead of the user manager@company.com, we get the default @WithMockUser : user ; but it doesn’t matter because we really care about his role: ROLE_MANAGER .
Conclusion
As you can see with annotations like @WithUserDetails and @WithMockUser , we can switch between different scripts of authenticated users without creating classes that are alienated from our architecture, just for simple tests. He also recommended that you see how @WithSecurityContext works for even more flexibility.