JSF
JSF

Reputation: 344

Spring Security - Multiple AbstractAuthenticationProcessingFilter's are executed even though first is successful

I have two filters added:

http
   .authorizeRequests()
   .antMatchers("/cron/**")
   .hasAnyRole(APP_ENGINE_CRON, SUPER_ADMIN);

http
   .authorizeRequests()
   .antMatchers("/**")
   .hasAnyRole(SERVICE, SUPER_ADMIN);
http
  .addFilterBefore(new AppengineCronRequestHeaderAuthFilter(authenticationManager(), "/cron/**"),
      UsernamePasswordAuthenticationFilter.class)
  .addFilterBefore(new GoogleIdTokenAuthFilter(authenticationManager(), "/**"),
      UsernamePasswordAuthenticationFilter.class);

Configured authentication manager:

@Override protected void configure(AuthenticationManagerBuilder auth) {
    auth.authenticationProvider(appengineCronAuthenticationProvider);
    auth.authenticationProvider(googleIdTokenAuthenticationProvider);
  }
import com.google.common.base.Strings;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;

public class AppengineCronRequestHeaderAuthFilter extends AbstractAuthenticationProcessingFilter {
  public static final String X_APP_ENGINE_CRON_HEADER = "X-Appengine-Cron";

  public AppengineCronRequestHeaderAuthFilter(AuthenticationManager authenticationManager,
      String processingUrl) {
    super(processingUrl);
    this.setAuthenticationManager(authenticationManager);
  }

  @Override public Authentication attemptAuthentication(HttpServletRequest request,
      HttpServletResponse response) throws AuthenticationException {
    var appengineCronHeader = request.getHeader(X_APP_ENGINE_CRON_HEADER);
    if (!Strings.isNullOrEmpty(appengineCronHeader)
        && Boolean.TRUE.equals(Boolean.valueOf(appengineCronHeader))) {
      return getAuthenticationManager().authenticate(new CronAuthentication());
    }
    return null;
  }
}

My understanding is that the first authentication provider which is successful should end the chain and proceed to execute the controller method with the provided authentication. The first filter yields an authentication, then the second filter is executed and yields a null authentication. At this point, the controller method is NOT executed, however, I'm getting a 200 response.

What am I missing here?

Upvotes: 1

Views: 467

Answers (1)

JSF
JSF

Reputation: 344

There were a few problems with my configuration.

  1. OOB AbstractAuthenticationProcessingFilter does not call chain.proceed(...) for a null Authentication. Thus if the first filter did not yield an authentication, the request would be terminated and a 200 would be returned.
  2. All processing filters should be smart about whether authentication is required, i.e. check the security context for an authentication before attempting its own authentication.
  3. The default WebSecurityConfigurerAdapter includes an exception handling configuration which is required to process AccessDenied exceptions that result from a lack of permissions after authentication in order for AccessDenied to terminate the request with a 403.

Upvotes: 1

Related Questions