title: Using Java config with Spring Security for annotation-based role checks and Basic HTTP Auth
body: <p>I'm trying to accomplish annotation-based (using <code>@PreAuthorize</code>) permission checking on one of my controllers. I'm using Java config, and have a <code>SecurityConfig</code> class extending <code>WebSecurityConfigurerAdapter</code>.</p>
  
  <p>I'm also using Basic HTTP Authentication to get to the methods that require authorization.</p>
  
  <p>But - the problem is, I <strong>don't</strong> want the <code>SecurityConfig</code> determining which methods require authentication using an <code>antPattern()</code> or similar. That's what the annotations are for!</p>
  
  <p>How can I configure my <code>SecurityConfig</code> class so that it properly uses HTTP Basic authentication and the configured <code>AuthenticationManager</code> for methods with <code>@PreAuthorize</code>, and allows anonymous access to controller methods that <em>do not</em> have any security annotations attached?</p>
  
  <p><strong>When I try this (Attempt #1):</strong></p>
  
  <pre><code>@Configuration
  @EnableWebSecurity
  @EnableWebMvcSecurity
  @EnableGlobalMethodSecurity(prePostEnabled = true)
  public class SecurityConfig extends WebSecurityConfigurerAdapter {
  
      @Override
      protected void configure(HttpSecurity http) throws Exception {
          http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
      }
  
      @Autowired
      public void registerGlobal(AuthenticationManagerBuilder auth) throws Exception {
          auth.inMemoryAuthentication().withUser("craig").password("craigpass").roles("USER");
      }
  }
  </code></pre>
  
  <p>My unprotected methods fail with:</p>
  
  <p><code>Full authentication is required to access this resource</code></p>
  
  <p>And my protected (but properly-authenticating) tests fail with:</p>
  
  <p><code>Error: Expected CSRF token not found. Has your session expired?</code></p>
  
  <p><strong>When I try this (Attempt #2):</strong></p>
  
  <pre><code>@Configuration
  @EnableWebSecurity
  @EnableWebMvcSecurity
  @EnableGlobalMethodSecurity(prePostEnabled = true)
  public class SecurityConfig extends WebSecurityConfigurerAdapter {
  
      @Autowired
      public void registerGlobal(AuthenticationManagerBuilder auth) throws Exception {
          auth.inMemoryAuthentication().withUser("craig").password("craigpass").roles("USER");
      }
  }
  </code></pre>
  
  <p>My unprotected methods fail with a <code>302</code> status code, and redirect to <code>/login</code>, and my protected/authenticating methods fail with:</p>
  
  <p><code>Error: Expected CSRF token not found. Has your session expired?</code></p>
  
  <p><strong>When I try this (Attempt #3):</strong></p>
  
  <pre><code>@Configuration
  @EnableWebMvcSecurity
  @EnableGlobalMethodSecurity(prePostEnabled = true)
  public class SecurityConfig extends WebSecurityConfigurerAdapter {
  
      @Override
      protected void configure(HttpSecurity http) throws Exception {
          http.csrf().disable().authorizeRequests().anyRequest().authenticated().and().httpBasic();
      }
  
      @Autowired
      public void registerGlobal(AuthenticationManagerBuilder auth) throws Exception {
          auth.inMemoryAuthentication().withUser("craig").password("craigpass").roles("USER");
      }
  }
  </code></pre>
  
  <p>My authenticating method works properly. (Woo hoo!) But, my unprotected method fails with <code>401</code> because my <code>HttpSecurity</code> has seemingly been configured to <em>only</em> allow access if you're authenticated - regardless of whether the controller method is annotated with <code>@PreAuthorize</code>.</p>
  
  <p><strong>When I try this (Attempt #4):</strong></p>
  
  <pre><code>@Configuration
  @EnableWebSecurity
  @EnableWebMvcSecurity
  @EnableGlobalMethodSecurity(prePostEnabled = true)
  public class SecurityConfig extends WebSecurityConfigurerAdapter {
  
      @Override
      protected void configure(HttpSecurity http) throws Exception {
          http.csrf().disable().authorizeRequests().anyRequest().permitAll();
      }
  
      @Autowired
      public void registerGlobal(AuthenticationManagerBuilder auth) throws Exception {
          auth.inMemoryAuthentication().withUser("craig").password("craigpass").roles("USER");
      }
  }
  </code></pre>
  
  <p>My unprotected methods work properly (allow access), but my authenticating methods (whether they have the proper HTTP auth or not) fail with status code <code>403</code>:</p>
  
  <p><code>Error: Access Denied</code></p>
  
  <p><strong>My authenticating test method</strong> that fails with <code>403</code> uses <code>MockMvc</code> and sends in a request using headers:</p>
  
  <p><code>{Content-Type=[application/json], Accept=[application/json], Authorization=[Basic Y3JhaWc6Y3JhaWdwYXNz]}</code></p>
  
  <p>Which I have decoded and verified, and a URL <code>/api/item</code> with method <code>POST</code>, which should target the corresponding method:</p>
  
  <pre><code>@RequestMapping(value = "/api/item", method = { RequestMethod.POST })
  @PreAuthorize("hasRole('ROLE_USER')")
  public void postNewItem(HttpServletRequest request, HttpServletResponse response) {
      ...
  }
  </code></pre>