3

I am using spring-boot-1.5.10 and spring-boot-starter-security. In my microservice, I am exposing API's to the external world and internal microservices. so I would like to 2-kind of security. one for external calls and other for internal calls.

I have referred this URL and tried to implement multiple security adapters in my application. But no luck it's always picking the internal one instead of external one,

Please find the security adapter for your reference,

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired(required = false)
    ServiceWebSecurityConfigurer serviceWebSecurityConfigurer;

//    @Override
//    public void configure(WebSecurity web) throws Exception {
//        web
//                .ignoring()
//                .antMatchers(HttpMethod.PUT,"/v1/emp/**")
//                .antMatchers(HttpMethod.DELETE,"/v1/emp/**");
//    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authenticationProvider(new ExternalApiAuthenticationProvider())
                .securityContext()
                .securityContextRepository(new ExternalApiSecurityContextRepository())
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(new ApiAuthenticationEntrypoint())
                .and()
                .httpBasic().disable()
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/v1/**").fullyAuthenticated();
        if(serviceWebSecurityConfigurer != null)
            serviceWebSecurityConfigurer.configure(http);
        http.authenticationProvider(new InternalApiAuthenticationProvider())
            .securityContext()
            .securityContextRepository(new InternalApiSecurityContextRepository())
            .and()
            .exceptionHandling()
            .authenticationEntryPoint(new ApiAuthenticationEntrypoint())
            .and()
            .httpBasic().disable()
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeRequests()
            .antMatchers(HttpMethod.PUT,"/v1/emp/**").fullyAuthenticated()
            .antMatchers(HttpMethod.DELETE,"/v1/emp/**").fullyAuthenticated();
    }
}

It always picks the "InternalApiSecurityContextRepository" even the external API's using internal security. It seems the later is overriding the former.

UPDATE-1(as per Gaurav Srivastav answer)

External API call security adapter :

@EnableWebSecurity
public class WebSecurityConfig {

    @Configuration
    @Order(2)
    public static class InternalSecurityConfig extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authenticationProvider(new InternalApiAuthenticationProvider())
                .securityContext()
                .securityContextRepository(new InternalApiSecurityContextRepository())
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(new InternalApiAuthenticationEntrypoint())
                .and()
                .httpBasic().disable()
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.PUT,"/v1/emp/**").fullyAuthenticated()
                .antMatchers(HttpMethod.DELETE,"/v1/emp/**").fullyAuthenticated();
        }
    }

    @Configuration
    @Order(1)
    public static class ExternalSecurityConfig extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authenticationProvider(new ExternalApiAuthenticationProvider())
                .securityContext()
                .securityContextRepository(new ExternalApiSecurityContextRepository())
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(new ApiAuthenticationEntrypoint())
                .and()
                .httpBasic().disable()
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/v1/**").fullyAuthenticated();
        }
    }
}

It works for External(Since the order is 1) but for internal we are getting the following exception and it is using the External configuration security context,

An internal server error occurred.Message:An Authentication object was not found in the SecurityContext

I think the problem here is, we cannot use 2-security context it seems.Is there anyway to use different security context?

Any hint would be really appreciable to solve the issue. Thanks in Advance.

VelNaga
  • 3,277
  • 5
  • 42
  • 74
  • Did you try to use `@Order(value = Ordered.HIGHEST_PRECEDENCE)` and `@Order(value = Ordered.LOWEST_PRECEDENCE)` to override? – Hasan Can Saral Oct 02 '18 at 10:48
  • @HasanCanSaral I have only one WebSecurityConfigurerAdapter and would like to configure Highest or Lowest? – VelNaga Oct 02 '18 at 10:50

1 Answers1

3

You have define more than one configuration and specify the order using @Order annotation.

Internal Configuration with its own authentication provider and url pattern.

@EnableWebSecurity
public class MultiHttpSecurityConfig {

    @Configuration
    @Order(1)
    public static class InternalSecurityConfig extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.antMatcher("/internal/**")
                .authorizeRequests().anyRequest().hasRole("ADMIN")
                .and().httpBasic().authenticationEntryPoint(authenticationEntryPoint());
        }
    }

    @Configuration
    @Order(2)
    public static class ExternalSecurityConfig extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.antMatcher("/external/**")
                .authorizeRequests().anyRequest().hasRole("ADMIN")
                .and().httpBasic().authenticationEntryPoint(authenticationEntryPoint());
        }
    }

Get more detail through using below article. https://www.baeldung.com/spring-security-multiple-entry-points

Felipe Desiderati
  • 1,614
  • 2
  • 17
  • 38
Gaurav Srivastav
  • 2,171
  • 1
  • 13
  • 17
  • Thanks a lot for your answer.order-1 is working fine but it's not picking order(2) call...I have updated the answer could you please have a look and let us know for any issue? – VelNaga Oct 02 '18 at 11:08
  • I have updated the answer you have to put the both the static classes inside @EnableSecurity. Try to use it. – Gaurav Srivastav Oct 02 '18 at 11:18
  • It's still the same...It's always going to the Order(1) security config. – VelNaga Oct 02 '18 at 12:50
  • key point to note here is, i would like to use multiple securityContext(one for external & another for internal) – VelNaga Oct 02 '18 at 13:17
  • If anyone is having the same issue, please take a look on this link: https://github.com/spring-projects/spring-security/issues/5593 – Ventrue Jul 02 '19 at 09:48
  • to sum up the above link, the most specific configuration has to be run first (e.g. configuration for /admin/** has to have Order(1) and configuration for everything else has to have Order(2), moreover, the most specific configuration has to start with antMatcher("/admin/**").authorizeRequests()..... (etc.) - this way you won't need to use NegatedRequestMatcher for the more general configuration, and antMatchers("/admin/**") after authorizeRequests() is not needed - you can use anyRequest() in that place. – hello_earth Sep 22 '21 at 15:08