title: Spring security 5 api key filter
body: <p>I'm trying to create a filter with spring security 5 and boot 2 that protects some but no all endpoints with an API Key and without sessions.
  However after the filter authenticates it tires to redirect to "/" instead of the original url as SavedRequestAwareAuthenticationSuccessHandler has an empy request cache and falls back to the default "/".</p>
  
  <p>How can I make it to carry on to the targeted resource instead of '/' and why it worked like this?</p>
  
  <p>I'd also appreciate if someone could explain to me why AbstractPreAuthenticatedProcessingFilter is designed to carry on with the filter chain after a sucessfull authentication but AbstractAuthenticationProcessingFilter is not.</p>
  
  <p>This is the security config class:</p>
  
  <pre class="lang-java prettyprint-override"><code>@Configuration
  @Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
  public class SecurityConfig extends WebSecurityConfigurerAdapter {
  
      @Value("${secret.admin.api.key}")
      String validApiKey;
  
      @Override
      protected void configure(AuthenticationManagerBuilder builder) throws Exception {
          builder.authenticationProvider(new ApiKeyAuthenticationProvider(validApiKey));
      }
  
      @Override
      protected void configure(HttpSecurity security) throws Exception {
          security
              .csrf().disable()
              .sessionManagement()
                  .sessionCreationPolicy(STATELESS)
                  .and()
              .addFilterBefore(new ApiKeyAuthenticationFilter(authenticationManager()), AnonymousAuthenticationFilter.class)
              .requestMatchers()
                  .antMatchers("/v1/admin/**")
                  .antMatchers("/actuator/**")
                  .and()
              .authorizeRequests().anyRequest().authenticated();
      }
  
  }
  
  </code></pre>
  
  <p>My filter matches requests that have Authorization header with a custom value. Extracts the api key from the header and passes the token with the principal and credentials(api key) to the authentication manager.</p>
  
  <pre class="lang-java prettyprint-override"><code>public class ApiKeyAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
  
  //...
  
      @Override
      public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
          throws AuthenticationException, IOException, ServletException {
          var apiKey = extractApiKey(request.getHeader(AUTHORIZATION));
          var token = new ApiKeyAuthenticationToken(apiKey);
          var authentication = getAuthenticationManager().authenticate(token);
          return authentication;
      }
  
  //...
  }
  </code></pre>
  
  <p>The following class validates the credentials and creates and authenticated token with a principal  and without credentials:</p>
  
  <pre class="lang-java prettyprint-override"><code>public class ApiKeyAuthenticationProvider implements AuthenticationProvider {
  
  // ...
  
      @Override
      public Authentication authenticate(Authentication authentication) throws AuthenticationException {
          var apiKey = (String) authentication.getCredentials();
          if (validApiKey.equals(apiKey)) {
              var auth = new ApiKeyAuthenticationToken();
              auth.setAuthenticated(true);
              return auth;
          } else {
              throw new BadCredentialsException("Bad ApiKey credentials");
          }
      }
  
      @Override
      public boolean supports(Class&lt;?&gt; authentication) {
          return ApiKeyAuthenticationToken.class.isAssignableFrom(authentication);
      }
  
  }
  </code></pre>
  
  <p>And the logs:</p>
  
  <pre><code>OrRequestMatcher: Trying to match using Ant [pattern='/v1/admin/**']
  AntPathRequestMatcher: Checking match of request : '/v1/admin/namespace'; against '/v1/admin/**'
  OrRequestMatcher: matched
  FilterChainProxy$VirtualFilterChain: /v1/admin/namespace at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
  FilterChainProxy$VirtualFilterChain: /v1/admin/namespace at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
  FilterChainProxy$VirtualFilterChain: /v1/admin/namespace at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
  FilterChainProxy$VirtualFilterChain: /v1/admin/namespace at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
  [...]
  FilterChainProxy$VirtualFilterChain: /v1/admin/namespace at position 5 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
  FilterChainProxy$VirtualFilterChain: /v1/admin/namespace at position 6 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
  FilterChainProxy$VirtualFilterChain: /v1/admin/namespace at position 7 of 11 in additional filter chain; firing Filter: 'ApiKeyAuthenticationFilter'
  ProviderManager: Authentication attempt using ....security.ApiKeyAuthenticationProvider
  AbstractAuthenticationTargetUrlRequestHandler: Using default Url: /
  DefaultRedirectStrategy: Redirecting to '/'
  </code></pre>
  
  <p>I tried to make this work based on <a href="https://spring.io/guides/topicals/spring-security-architecture" rel="nofollow noreferrer">this</a>.</p>