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