0

I've got a SpringBoot REST server working (with HTTPS mutual authentication). HTTP Get requests are working, and now I'm trying to HTTP Post requests working.

When I make a POST I get the error

curl $CURL_OPTS -X POST https://$CURL_HOSTS/api/my/endpoint1
{
    "error": "Forbidden",
    "path": "api/my/endpoint1",
    "status": 403,
    "timestamp": "2023-05-12T19:47:58.874+00:00"
}

I searched and found this solution: How to Solve 403 Error in Spring Boot Post Request

But after adding the code that is recommended in that solution, I get the error:

2023-05-12 21:22:00 UTC WARN  o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext -
Exception encountered during context initialization - cancelling
refresh attempt: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'springSecurityFilterChain' defined
in class path resource
[org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: 
Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: 
Failed to instantiate [javax.servlet.Filter]: Factory method 'springSecurityFilterChain' threw exception; 
nested exception is java.lang.IllegalStateException: 
Found WebSecurityConfigurerAdapter as well as SecurityFilterChain.
Please select just one.

Below are the two classes that conflicting and causing this error. I'm searching for how incorporate what is needed from both of them either in the same file or in the same bean, I'm not sure what solution is needed.

Original File: Supports X509 and mutual authentication

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.SecurityFilterChain;

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Slf4j
public class X509AuthenticationServer {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .x509()
                .subjectPrincipalRegex("CN=(.*?)(?:,|$)")
                .userDetailsService(userDetailsService());
        log.info("X509AuthenticationServer: filterChain bean created");
        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return new UserDetailsService() {
            @Override
            public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                // Allow any user to be valid
                return new User(username, "",
                        AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
            }
        };
    }
}

Newly added file based on How to Solve 403 Error in Spring Boot Post Request

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;


@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

    @Override
    protected void configure(HttpSecurity http) throws Exception{
        http.cors().and().csrf().disable();
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("*"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

}

Most of this was just copy/pasted from another project so a pointer to the SpringBoot or SpringWeb docs that explain this might also be helpful.

So far I haven't found anything combining these two issue so most likely it's a Spring concept I need to get clear in my brain.

NOTE: I have started trying combining the two files but so far haven't found a working solution.

Spring docs

PatS
  • 8,833
  • 12
  • 57
  • 100

1 Answers1

0

The problem is caused by the code that is trying to define two SecurityFilterChain beans. One inside the first code file and the 2nd inside the 2nd code file. You'll need to combine the code that deals with the HttpSecurity object into a single method. For example,

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable();
        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .x509()
                .subjectPrincipalRegex("CN=(.*?)(?:,|$)")
                .userDetailsService(userDetailsService());
        log.info("X509AuthenticationServer: filterChain bean created");
        return http.build();
    }

This happens because the code to extend WebSecurityConfigurerAdapter likley creates a Filter bean. I see the following decompilled code when I look at it inside IntelliJ.

    public void init(WebSecurity web) throws Exception {
        HttpSecurity http = this.getHttp();
        web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
            FilterSecurityInterceptor securityInterceptor = (FilterSecurityInterceptor)http.getSharedObject(FilterSecurityInterceptor.class);
            web.securityInterceptor(securityInterceptor);
        });
    }
PatS
  • 8,833
  • 12
  • 57
  • 100