title: Spring-Security: Return Status 401 When AuthenticationManager Throws BadCredentialsException
body: <p>First of all, I'd like to point that I don't know Spring Security very much, <em>actually I know quite little about its interfaces and classes</em>, but I got a not so simple task to do and can't quite figure it out. My code is based in the following post in the Spring Security Forum (I'm not having the same problem as the post owner):
  <a href="http://forum.spring.io/forum/spring-projects/security/747178-security-filter-chain-is-always-calling-authenticationmanager-twice-per-request" rel="noreferrer">http://forum.spring.io/forum/spring-projects/security/747178-security-filter-chain-is-always-calling-authenticationmanager-twice-per-request</a></p>
  
  <p>I'm programming a Spring MVC system which will serve HTTP content but, in order to do so, it has a preauth check (which I'm currently using <a href="http://docs.spring.io/spring-security/site/docs/current-SNAPSHOT/apidocs/org/springframework/security/authentication/AuthenticationManager.html" rel="noreferrer">RequestHeaderAuthenticationFilter</a> with a custom <a href="http://docs.spring.io/spring-security/site/docs/current-SNAPSHOT/apidocs/org/springframework/security/authentication/AuthenticationManager.html" rel="noreferrer">AuthenticationManager</a>).</p>
  
  <p>To authorize the user, I'll check the token against two sources, a Redis cache "database" and Oracle. If the token is not found in any of those sources, the authenticate method of my custom AuthenticationManager throws a BadCredentialsException (which I believe honours the AuthenticationManager contract).</p>
  
  <p>Now I'd like to return in the HTTP response 401 - Unauthorized, but Spring keeps returning 500 - Server Error. Is it possible to customize my setup to return only 401 not 500?</p>
  
  <p>Here's the relevant code:</p>
  
  <p>SecurityConfig - main spring security config</p>
  
  <pre class="lang-java prettyprint-override"><code>package br.com.oiinternet.imoi.web.config;
  
  import javax.validation.constraints.NotNull;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.springframework.boot.autoconfigure.security.SecurityProperties;
  import org.springframework.context.ApplicationContext;
  import org.springframework.context.annotation.Bean;
  import org.springframework.context.annotation.Configuration;
  import org.springframework.core.annotation.Order;
  import org.springframework.http.HttpMethod;
  import org.springframework.security.authentication.AuthenticationManager;
  import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
  import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  import org.springframework.security.config.http.SessionCreationPolicy;
  import org.springframework.security.web.AuthenticationEntryPoint;
  import org.springframework.security.web.access.AccessDeniedHandler;
  import org.springframework.security.web.access.AccessDeniedHandlerImpl;
  import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
  import org.springframework.security.web.authentication.logout.LogoutFilter;
  import org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter;
  
  @Configuration
  @EnableWebSecurity
  @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
  public class SecurityConfig extends WebSecurityConfigurerAdapter {
  
      private static final Logger LOG = LoggerFactory.getLogger(SecurityConfig.class);
  
      public static final String X_AUTH_TOKEN = "X-Auth-Token";
  
      private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();
  
      @Bean
      public AuthenticationManager authenticationManager() {
          return new TokenBasedAuthenticationManager();
      }
  
      @Bean
      public AuthenticationEntryPoint authenticationEntryPoint() {
          return new Http403ForbiddenEntryPoint();
      }
  
      @Bean
      public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter(
              final AuthenticationManager authenticationManager) {
          RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter();
          filter.setAuthenticationManager(authenticationManager);
          filter.setExceptionIfHeaderMissing(false);
          filter.setPrincipalRequestHeader(X_AUTH_TOKEN);
          filter.setInvalidateSessionOnPrincipalChange(true);
          filter.setCheckForPrincipalChanges(true);
          filter.setContinueFilterChainOnUnsuccessfulAuthentication(false);
          return filter;
      }
  
      /**
       * Configures the HTTP filter chain depending on configuration settings.
       *
       * Note that this exception is thrown in spring security headerAuthenticationFilter chain and will not be logged as
       * error. Instead the ExceptionTranslationFilter will handle it and clear the security context. Enabling DEBUG
       * logging for 'org.springframework.security' will help understanding headerAuthenticationFilter chain
       */
      @Override
      protected void configure(final HttpSecurity http) throws Exception {
          RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter = fromContext(http,
                  RequestHeaderAuthenticationFilter.class);
  
          AuthenticationEntryPoint authenticationEntryPoint = fromContext(http, AuthenticationEntryPoint.class);
  
          http.authorizeRequests()
              .antMatchers(HttpMethod.GET, "/auth").permitAll()
              .antMatchers(HttpMethod.GET, "/**").authenticated()
              .antMatchers(HttpMethod.POST, "/**").authenticated()
              .antMatchers(HttpMethod.HEAD, "/**").authenticated()
          .and()
              .sessionManagement()
                  .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
          .and().securityContext()
          .and().exceptionHandling()
                  .authenticationEntryPoint(authenticationEntryPoint)
                  .accessDeniedHandler(accessDeniedHandler)
          .and()
              .addFilterBefore(requestHeaderAuthenticationFilter, LogoutFilter.class);
      }
  
      private &lt;T&gt; T fromContext(@NotNull final HttpSecurity http, @NotNull final Class&lt;T&gt; requiredType) {
          @SuppressWarnings("SuspiciousMethodCalls")
          ApplicationContext ctx = (ApplicationContext) http.getSharedObjects().get(ApplicationContext.class);
          return ctx.getBean(requiredType);
      }
  }
  </code></pre>
  
  <p>TokenBasedAuthenticationManager - my custom AuthenticationManager</p>
  
  <pre class="lang-java prettyprint-override"><code>package br.com.oiinternet.imoi.web.config;
  
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.security.authentication.AuthenticationManager;
  import org.springframework.security.authentication.BadCredentialsException;
  import org.springframework.security.core.Authentication;
  import org.springframework.security.core.AuthenticationException;
  
  import br.com.oi.oicommons.lang.message.Messages;
  import br.com.oiinternet.imoi.service.AuthService;
  import br.com.oiinternet.imoi.web.security.auth.AuthenticationAuthorizationToken;
  
  public class TokenBasedAuthenticationManager implements AuthenticationManager {
  
      @Autowired
      private AuthService authService;
  
      @Autowired
      private Messages messages;
  
      @Override
      public Authentication authenticate(Authentication authentication) throws AuthenticationException {
  
          final String token = (String) authentication.getPrincipal();
  
          if (authService.isAuthorized(token) || authService.authenticate(token)) {
              return new AuthenticationAuthorizationToken(token);
          } 
              throw new BadCredentialsException(messages.getMessage("access.bad.credentials"));
      }
  
  }
  </code></pre>
  
  <p>Example of request/response cycle using curl:</p>
  
  <pre class="lang-json prettyprint-override"><code>user@user-note:curl --header "X-Auth-Token: 2592cd35124dc3d79bdd82407220a6ea7fad9b8b313a1205cf1824a5ce726aa8dd763cde8c05faadae48b47252de95b0" http://localhost:8081/test/auth -v
  * Hostname was NOT found in DNS cache
  *   Trying 127.0.0.1...
  * Connected to localhost (127.0.0.1) port 8081 (#0)
  &gt; GET /test/auth HTTP/1.1
  &gt; User-Agent: curl/7.35.0
  &gt; Host: localhost:8081
  &gt; Accept: */*
  &gt; X-Auth-Token: 2592cd35124dc3d79bdd82407220a6ea7fad9b8b313a1205cf1824a5ce726aa8dd763cde8c05faadae48b47252de95b0
  &gt; 
  &lt; HTTP/1.1 500 Server Error
  &lt; X-Content-Type-Options: nosniff
  &lt; X-XSS-Protection: 1; mode=block
  &lt; Pragma: no-cache
  &lt; X-Frame-Options: DENY
  &lt; Content-Type: application/json;charset=UTF-8
  &lt; Connection: close
  * Server Jetty(9.1.0.v20131115) is not blacklisted
  &lt; Server: Jetty(9.1.0.v20131115)
  &lt; 
  * Closing connection 0
  {"timestamp":1414513379405,"status":500,"error":"Internal Server Error","exception":"org.springframework.security.authentication.BadCredentialsException","message":"access.bad.credentials","path":"/test/auth"}
  </code></pre>