Eric Rybarczyk

Eric Rybarczyk

Enthusiastic and motivated software engineer with diverse experience

AWS & Java Certified Developer

Mock Custom User

Building a Mock object for custom UserDetails implementations in Spring Security

Eric Rybarczyk

spring security guard
Feature photo by Liam Tucker on Unsplash

While working through the Spring Security aspects of my SpringBikeClinic.com project, I implemented SecurityUser, a custom UserDetails object which was composed with a User database entity. For unit testing my @Controller code, I wanted a clean way to provide a mock instance of my custom user type.

There are a few annotations provided by Spring Security that allow us to provide a user when tests are run:

If we are simply using the default User object from Spring Security, the @WithMockUser is the easy choice. This option allows us to customize the username as well as the roles or authorities used for the test method or class. Another option, @WithUserDetails, is flexible but requires your user to exist and be accessible from a UserDetailsService instance.

It can also be useful to test using the @WithAnonymousUser annotation, either for areas that are intended to be used by unauthenticated users, or to verify than anonymous users are blocked from functionality that requires authentication. For example:

@Test
void getAccountDetails_asAnonymousUser_isForbidden() throws Exception {
  mockMvc.perform(get("/your/path/here"))
         .andExpect(status().isUnauthorized());
  verifyNoInteractions(someService);
}

However, when your tests require a specific, custom Authentication type, we can define our own annotation:

@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithMockCustomUserUserSecurityContextFactory.class)
public @interface WithMockCustomUser {
  String username() default "user";
  String password() default "pwd";
  String firstName() default "Firstname";
  String lastName() default "Lastname";
}

By applying @WithSecurityContext to this @WithMockCustomUser annotation, Spring Security will use the specified WithSecurityContextFactory to instantiate a SecurityContext.

public class WithMockCustomUserUserSecurityContextFactory 
               implements WithSecurityContextFactory<WithMockCustomUser> {
  @Override
  public SecurityContext createSecurityContext(WithMockCustomUser annotation) {
    SecurityContext context = SecurityContextHolder.createEmptyContext();
    Authority authorityCustomer = Authority.builder().role("CUSTOMER").build();
    String password = "{noop}password";
    User principal = User.builder()
                         .id(1L)
                         .email("user@yourdomain.com")
                         .password(password)
                         .firstName("Firsty")
                         .lastName("McLasty")
                         .authority(authorityCustomer)
                         .build();
    SecurityUser securityUser = new SecurityUser(principal);
    Authentication auth = new UsernamePasswordAuthenticationToken(
                                    securityUser, password, securityUser.getAuthorities() );
    context.setAuthentication(auth);
    return context;
  }
}

With these annotations defined, we can simply annotate our test methods with @WithMockCustomUser and know that we will have a valid instance of our custom user type provided by Spring Security. For example:

@WithMockCustomUser
@Test
void getAccountDetails_asAuthenticatedUser_isOk() throws Exception {
  mockMvc.perform(get("/your/path/here"))
         .andExpect(status().isOk())
         .andExpect(view().name("yourViewName"));
}

Article Series

Software Projects

Recent Posts

Categories

About

Enthusiastic and motivated software engineer