3

After upgrading org.springframework.boot from 2.5.6 to 2.6.2 version the following error appears on application startup:

***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
|  securityConfiguration
↑     ↓
|  org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration
└─────┘

I know from other questions that the easy workaround is to use the option allow-cirular-references, but I would like to really solve the problem rather than use a workaround.

This is my SecurityConfiguration class:

package com.mycompany.myapp.servicex.configuration;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Value("${application.myapp.in-browser-allowed-origins}")
    private String[] inBrowserAllowedOrigins;

    private final String[] inBrowserAllowedMethods = new String[]{"POST", "OPTIONS"};

    @Override
    protected void configure(final HttpSecurity httpSecurity) throws Exception {
        httpSecurity.cors()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .csrf().disable()
                .formLogin().disable()
                .httpBasic().disable()
                .logout().disable();
    }

    @Bean
    public WebMvcConfigurer corsConfigurer()
    {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/in-browser/login")
                        .allowedOrigins(inBrowserAllowedOrigins)
                        .allowedMethods(inBrowserAllowedMethods);
                registry.addMapping("/**").allowedOrigins();
            }
        };
    }
}


Does anybody know how can I programmatically break the cyclic dependency?

I was already playing around with the @Lazy annotation on the corsConfigurer bean and in the inBrowserAllowedOrigins attribute with no success.

  • 3
    I would guess separate the class into two, with one being the config containing the bean, the other being the configurerAdapter with the configure method. – daniu Feb 10 '22 at 11:28
  • 1
    why would you use the WebMVC AUTO configuration, when you override the configuration with your own implementation? i think you should remove the annotation @EnableWebMvcConfiguration on the Application class. – Tom Elias Feb 10 '22 at 11:33
  • Move your `corsConfigurer` bean to a seperate configuration. Or just create a seperate bean which implements `WebMvcConfigurer` and mark it with `@Configuration`. – M. Deinum Mar 10 '22 at 13:14

1 Answers1

4

Instead of using an inner bean for the WebMvcConfigurer make it a proper class which you can annotate with @Configuration.

@Configuration
public class WebConfiguration implements WebMvcConfigurer {

    @Value("${application.myapp.in-browser-allowed-origins}")
    private String[] inBrowserAllowedOrigins;

    private final String[] inBrowserAllowedMethods = new String[]{"POST", "OPTIONS"};

 @Override
 public void addCorsMappings(CorsRegistry registry) {
   registry.addMapping("/in-browser/login")
     .allowedOrigins(inBrowserAllowedOrigins)
     .allowedMethods(inBrowserAllowedMethods);
   registry.addMapping("/**").allowedOrigins();
  }
}

Now you can remove the corsConfiguration() @Bean method from your security configuration and the circular dependency is gone.

Or another option is to use Spring Security to configure CORS instead of Spring MVC. This is explained in the Spring Security Reference Guide.

For that you need to modify your security configuration

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Value("${application.myapp.in-browser-allowed-origins}")
    private String[] inBrowserAllowedOrigins;

    private final String[] inBrowserAllowedMethods = new String[]{"POST", "OPTIONS"};

    @Override
    protected void configure(final HttpSecurity httpSecurity) throws Exception {
        httpSecurity.cors()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .csrf().disable()
                .formLogin().disable()
                .httpBasic().disable()
                .logout().disable()
                .cors(withDefaults());
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
      CorsConfiguration conf = new CorsConfiguration();      
      conf.setAllowedOrigins(Arrays.asList(inBrowserAllowedOrigins));       
      conf.setAllowedMethods(Arrays.asList(inBrowserAllowedMethods));
      UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/in-browser/login", configuration);
        source.registerCorsConfiguration("/**", new CorsConfiguration());
        return source;       
    }
}

Something like this should work as well, as now Spring Security is in full control.

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
  • 1
    It works for me, I tryed using "allow-bean-definition-overriding: true" or "allow-circular-references: true" but it does not work, because apparently it was executed diffrent in the container, so the real solution is remove the cyclic dependency – Victor David Francisco Enrique Apr 08 '22 at 21:25