Home > Mobile >  API Key based authentication for a single endpoint
API Key based authentication for a single endpoint

Time:07-19

In my spring boot application, I need two types of authentication mechanisms. For file uploading endpoint, it requires a api-key based authentication and for the other endpoints it requires a username password based authentication. Previously, only the username password based authentication there as below.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.firewall.DefaultHttpFirewall;
import org.springframework.security.web.firewall.HttpFirewall;

//@Slf4j
/**
 * This class is used to setup security.
 * 
 *
 *
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  private static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfig.class);

  @Autowired
  private CustomAuthenticationProvider customAuthProvider;

  /**
   * This method to configure HTTP Security.
   * 
   * @param http
   *          - HttpSecurity
   */
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    LOGGER.info("Entered in the actuator security cofigure method");

    http.csrf().disable().authorizeRequests().antMatchers("/actuator/*").permitAll().anyRequest()
        .authenticated().and().httpBasic();
  }

  // Ignore basic auth for WSDL URL
  // Ignore basic auth for SWAGGER
  /**
   * This method to configure web security.
   * 
   * @param web
   *          - WebSecurity
   */
  @Override
  public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/**/*.wsdl").antMatchers("/**/*.wsdl$*")
                  .antMatchers("/v2/api-docs");
    web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
  }

  /**
   * This method to configure Authentication Manager Builder.
   * 
   * @param auth
   *          - AuthenticationManagerBuilder
   */
  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(customAuthProvider);

  }

  @Bean
  public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
    DefaultHttpFirewall firewall = new DefaultHttpFirewall();
    firewall.setAllowUrlEncodedSlash(true);
    return firewall;
  }

}

I changed this code in order to support api-key based authentication as well below. Went through the below threads and implemented accordingly. Securing Spring Boot API with API key and secret

Spring Security : Multiple HTTP Config not working

Implementation:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.firewall.DefaultHttpFirewall;
import org.springframework.security.web.firewall.HttpFirewall;

// @Slf4j
/**
 * This class is used to setup security.
 *
 * 
 */
@EnableWebSecurity
public class MultiSecurityConfig extends WebSecurityConfigurerAdapter {

  private static final Logger LOGGER = LoggerFactory.getLogger(MultiSecurityConfig.class);

  /** This method to configure HTTP Security. */
  @Configuration
  @Order(2)
  public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {

    @Value("${http.auth-token-header-name}")
    private String principalRequestHeader;

    @Value("${http.auth-token}")
    private String principalRequestValue;

    protected void configure(HttpSecurity http) throws Exception {
      APIKeyAuthFilter filter = new APIKeyAuthFilter(principalRequestHeader);
      filter.setAuthenticationManager(
          new AuthenticationManager() {

            @Override
            public Authentication authenticate(Authentication authentication)
                throws AuthenticationException {
              String principal = (String) authentication.getPrincipal();
              if (!principalRequestValue.equals(principal)) {
                throw new BadCredentialsException(
                    "The API key was not found or not the expected value.");
              }
              authentication.setAuthenticated(true);
              return authentication;
            }
          });
      http.csrf().disable().antMatcher("/**/file").authorizeRequests().anyRequest().authenticated();
    }
  }

  @Order(1)
  @Configuration
  public static class LoginSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired private CustomAuthenticationProvider customAuthProvider;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
      LOGGER.info("Entered in the actuator security cofigure method");

      http.csrf()
          .disable()
          .authorizeRequests()
          .antMatchers("/actuator/*")
          .permitAll()
          .anyRequest()
          .authenticated()
          .and()
          .httpBasic();
    }

    // Ignore basic auth for WSDL URL
    // Ignore basic auth for SWAGGER
    /**
     * This method to configure web security.
     *
     * @param web - WebSecurity
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
      web.ignoring()
          .antMatchers("/**/*.wsdl")
          .antMatchers("/**/*.wsdl$*")
          .antMatchers("/v2/api-docs");
      web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
    }

    /**
     * This method to configure Authentication Manager Builder.
     *
     * @param auth - AuthenticationManagerBuilder
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
      auth.authenticationProvider(customAuthProvider);
    }

    @Bean
    public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
      DefaultHttpFirewall firewall = new DefaultHttpFirewall();
      firewall.setAllowUrlEncodedSlash(true);
      return firewall;
    }
  }
}

But when I upload a file to server using "/file" endpoint, it seems the api key based authentication does not catch it. It goes to login based authentication and returns a response as unauthorized.

Any help to fix?

CodePudding user response:

For the @Order anotation lower values have higher priority. meaning your username/password authentication triggers first. and said authentication has a .anyRequest() statement which result in it catching all requests.

The lower ordered configurations should catch specific endpoints while the highest is the default. In your case this would mean the API endpoint should be @Order(1) and the default should be @Order

Also you are not adding your filter to the http object in code so need to add that before it works

on a unrelated note i think you should remove extends WebSecurityConfigurerAdapter from the encapsulating class and @Configuration to it as it contains configuration properties that spring needs to pick up on boot but is not an instance of WebSecurityConfigurerAdapter as your subclasses now are

  • Related