Spring Boot Unit Tests with JWT Token Security

Viewed 72414

I am creating a backend using Spring Boot and I have just added JWT security to it.

I have done some tests using a REST Client and the JWT security is working fine, however all of my unit tests are now returning a 403 error code.

I've added the @WithMockUser annotation to them, but they are still not working:

@Test
@WithMockUser
public void shouldRedirectToInstaAuthPage() throws Exception {
    mvc.perform(MockMvcRequestBuilders.get("/instaAuth")).andExpect(status().is3xxRedirection());
}

Is there some other configuration that I am missing here?

Here is the security configuration:

@Configuration
@EnableWebSecurity
public class ServerSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
      protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests()
            .antMatchers("/").permitAll()
            .antMatchers(HttpMethod.POST, "/login").permitAll()
            .anyRequest().authenticated()
            .and()
            // We filter the api/login requests
            .addFilterBefore(new JWTLoginFilter("/login", authenticationManager()),
                    UsernamePasswordAuthenticationFilter.class)
            // And filter other requests to check the presence of JWT in header
            .addFilterBefore(new JWTAuthenticationFilter(),
                    UsernamePasswordAuthenticationFilter.class);
      }

      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // Create a default account
        auth.inMemoryAuthentication()
            .withUser("john")
            .password("123")
            .roles("ADMIN");
      }
}

And Method security:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        return new OAuth2MethodSecurityExpressionHandler();
    }

}
2 Answers

One thing you need to be aware of when testing using this createToken() method is that your tests cannot test for a nonexistent user.
This is because createToken() only makes a JWT token based off of the string you put into it.
If you want to make sure nonexistent users cannot gain access, I recommend making your createToken() method private and instead use requests to gain the token, like this:

@Test
public void existentUserCanGetTokenAndAuthentication() throws Exception {
    String username = "existentuser";
    String password = "password";

    String body = "{\"username\":\"" + username + "\", \"password\":\" 
                  + password + "\"}";

    MvcResult result = mvc.perform(MockMvcRequestBuilders.post("/v2/token")
            .content(body))
            .andExpect(status().isOk()).andReturn();

    String response = result.getResponse().getContentAsString();
    response = response.replace("{\"access_token\": \"", "");
    String token = response.replace("\"}", "");

    mvc.perform(MockMvcRequestBuilders.get("/test")
        .header("Authorization", "Bearer " + token))
        .andExpect(status().isOk());
}

In a similar way, you can show that a nonexistent user will not be able to get this result:

@Test
public void nonexistentUserCannotGetToken() throws Exception {
    String username = "nonexistentuser";
    String password = "password";

    String body = "{\"username\":\"" + username + "\", \"password\":\" 
                  + password + "\"}";

    mvc.perform(MockMvcRequestBuilders.post("/v2/token")
            .content(body))
            .andExpect(status().isForbidden()).andReturn();
}
Related