title: How to test JWT authentication with database-based users with Spring security?
body: <p>I have a small Spring Boot 2.1.6 webapp with JWT authententication. Call flow is as follows:</p>
  
  <ol>
  <li>User enters username and password and sends a POST request to /authenticate</li>
  <li>A filter is watching this URL (setFilterProcessesUrl), when a request comes, it hashes the password and checks it against the hash stored in DB</li>
  <li>If matches, and user is not locked, it creates a JWT with username and granted roles, and returns it in response</li>
  <li>User must include this JWT in all further requests</li>
  </ol>
  
  <p>Also, CSRF is disabled in the WebSecurityConfigurerAdapter.</p>
  
  <p>The solution itself is working fine, but I have to create unit tests as well. I ended up with the following test case:</p>
  
  <pre><code>@RunWith(SpringRunner.class)
  @WebMvcTest
  @ContextConfiguration(classes = { ConfigReaderMock.class })
  public class ControllerSecurityTest {
  
      private static final String VALID_USERNAME = "username";
      private static final String VALID_PASSWORD = "password";
  
      @Autowired
      private MockMvc mockMvc;
  
      private String createAuthenticationBody(String username, String passwordHash) {
          return "username=" + URLEncoder.encode(username, StandardCharsets.UTF_8) + "&amp;password="
                  + URLEncoder.encode(passwordHash, StandardCharsets.UTF_8);
      }
  
      @Test
      public void testValidLogin() throws Exception {
          MvcResult result = mockMvc
                  .perform(MockMvcRequestBuilders.post("/authenticate")
                          .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                          .content(createAuthenticationBody(VALID_USERNAME, VALID_PASSWORD)).accept(MediaType.ALL))
                  .andExpect(status().isOk()).andReturn();
  
          String authHeader = result.getResponse().getHeader(SecurityConstants.TOKEN_HEADER);
  
          mockMvc.perform(MockMvcRequestBuilders.get("/main?" + SecurityConstants.TOKEN_QUERY_PARAM + "="
                  + URLEncoder.encode(authHeader, StandardCharsets.UTF_8))).andExpect(status().isOk());
      }
  }
  </code></pre>
  
  <p>What I expect, is that the server accepts the username and password provided, and returns the JWT, which I can use in the subsequent request to access the next page (the same is implemented in the front end). Instead I get HTTP 403 from the authentication filter:</p>
  
  <pre><code>MockHttpServletRequest:
        HTTP Method = POST
        Request URI = /authenticate
         Parameters = {username=[username], password=[password]}
            Headers = [Content-Type:"application/x-www-form-urlencoded", Accept:"*/*"]
               Body = &lt;no character encoding set&gt;
      Session Attrs = {org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN=org.springframework.security.web.csrf.DefaultCsrfToken@4ac0fdc7}
  
  Handler:
               Type = null
  
  Async:
      Async started = false
       Async result = null
  
  Resolved Exception:
               Type = null
  
  ModelAndView:
          View name = null
               View = null
              Model = null
  
  FlashMap:
         Attributes = null
  
  MockHttpServletResponse:
             Status = 403
      Error message = Forbidden
            Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
       Content type = null
               Body =
      Forwarded URL = null
     Redirected URL = null
            Cookies = []
  </code></pre>
  
  <p>I noticed it is sending a CSRF token for some reason in the Session Attributes. Further checking the logs, I can see the belo messages:</p>
  
  <pre><code>2019-07-29 08:09:17,438 DEBUG o.s.b.f.s.DefaultSingletonBeanRegistry [main] Creating shared instance of singleton bean 'org.springframework.boot.autoconfigure.security.servlet.WebSecurityEnablerConfiguration'
  2019-07-29 08:09:17,443 DEBUG o.s.s.c.a.a.c.AuthenticationConfiguration$EnableGlobalAuthenticationAutowiredConfigurer [main] Eagerly initializing {org.springframework.boot.autoconfigure.security.servlet.WebSecurityEnablerConfiguration=org.springframework.boot.autoconfigure.security.servlet.WebSecurityEnablerConfiguration$$EnhancerBySpringCGLIB$$236da03c@4e68aede}
  2019-07-29 08:09:17,444 DEBUG o.s.b.f.s.DefaultSingletonBeanRegistry [main] Creating shared instance of singleton bean 'inMemoryUserDetailsManager'
  2019-07-29 08:09:17,445 DEBUG o.s.b.f.s.DefaultSingletonBeanRegistry [main] Creating shared instance of singleton bean 'org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration'
  2019-07-29 08:09:17,454 DEBUG o.s.b.f.s.DefaultSingletonBeanRegistry [main] Creating shared instance of singleton bean 'spring.security-org.springframework.boot.autoconfigure.security.SecurityProperties'
  2019-07-29 08:09:17,457 DEBUG o.s.b.f.s.ConstructorResolver [main] Autowiring by type from bean name 'inMemoryUserDetailsManager' via factory method to bean named 'spring.security-org.springframework.boot.autoconfigure.security.SecurityProperties'
  2019-07-29 08:09:17,462 INFO o.s.b.a.s.s.UserDetailsServiceAutoConfiguration [main] 
  
  Using generated security password: 963b2bac-d953-4793-a8cd-b3f81586823e
  
  ...
  
  2019-07-29 08:09:17,783 DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository [main] No HttpSession currently exists
  2019-07-29 08:09:17,784 DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository [main] No SecurityContext was available from the HttpSession: null. A new one will be created.
  2019-07-29 08:09:17,794 DEBUG o.s.s.w.c.CsrfFilter [main] Invalid CSRF token found for http://localhost/authenticate
  2019-07-29 08:09:17,795 DEBUG o.s.s.w.h.w.HstsHeaderWriter [main] Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@1c15a6aa
  2019-07-29 08:09:17,796 DEBUG o.s.s.w.c.HttpSessionSecurityContextRepository$SaveToSessionResponseWrapper [main] SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
  2019-07-29 08:09:17,799 DEBUG o.s.s.w.c.SecurityContextPersistenceFilter [main] SecurityContextHolder now cleared, as request processing completed
  </code></pre>
  
  <p>So it seems like Spring Security is creating it's own security configuration, instead of using the class I created, extending WebSecurityConfigurerAdapter. Question is, why? And how can I force it to use my security config, as I'm dependent on it with the database login?</p>
  
  <p>Update: added WebSecurityConfigurerAdapter</p>
  
  <pre><code>@EnableWebSecurity
  @EnableGlobalMethodSecurity(securedEnabled = true)
  public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  
      @Autowired
      private AICAuthenticationService authenticationService;
  
      @Autowired
      private AICUserDetailsService aicUserDetailsService;
  
      @Autowired
      private AICLogoutSuccessHandler aicLogoutSuccessHandler;
  
      @Override
      public void configure(HttpSecurity http) throws Exception {
          http
                  .csrf().disable()
                  .cors()
                  .and()
                  .authorizeRequests()
                          .antMatchers("/resources/**", "/login", "/").permitAll()
                          .anyRequest().authenticated()
                  .and()
                          .addFilter(new JwtAuthenticationFilter(authenticationManager()))
                          .addFilter(new JwtAuthorizationFilter(authenticationManager()))
                  .sessionManagement()
                          .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                  .and()
                  .logout()
                          .logoutUrl("/logout")
                          .logoutSuccessHandler(aicLogoutSuccessHandler)
                          .invalidateHttpSession(true)
                          .deleteCookies("JSESSIONID", "error");
      }
  
      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          auth.userDetailsService(aicUserDetailsService);
      }
  
      @Override
      protected AuthenticationManager authenticationManager() throws Exception {
          return authenticationService;
      }
  
      @Bean
      public AuthenticationManager custromAuthenticationManager() throws Exception {
          return authenticationManager();
      }
  
      @Autowired
      public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
          auth.userDetailsService(aicUserDetailsService);
      }
  </code></pre>