12

I am getting below error message when I am running my spring boot application.

Description:

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

┌─────┐
|  securityConfiguration (field private com.prity.springbootdemo1.service.UserService com.prity.springbootdemo1.config.SecurityConfiguration.userService)
↑     ↓
|  userServiceImpl (field private org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder com.prity.springbootdemo1.service.UserServiceImpl.passwordEncoder)
└─────┘


Action:

Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
M. Deinum
  • 115,695
  • 22
  • 220
  • 224
prity sinha
  • 121
  • 1
  • 1
  • 6
  • 3
    Fix your code to not need a circular dependency. Apparently you have your own `UserService`, which propably also acts as a `UserDetailsService` which you need in the configuration class. Which is a problem as that configuration class also creates the `PasswordEncoder` you need. To fix remove the need of the circular dependency, I would suggest making a separate `UserDetailsService` which only does that and doesn' tneed the `PasswordEncoder`, for the rest you can use your `UserService` as is. – M. Deinum Apr 14 '22 at 06:21
  • Thank you. I will try to implement in this way. – prity sinha Apr 14 '22 at 06:33
  • 2
    @pritysinha seeing you have circular reference with passwordEncoder and security configuration, I believe you will need the same solution as described here https://stackoverflow.com/a/71527547/7237884 . This probably happened with update to spring boot 2.6.0 – Panagiotis Bougioukos Apr 14 '22 at 07:30

7 Answers7

10

You can try with this. Add it to file application.properties

spring.main.allow-circular-references=true

And try to run. This is not best solution you still need to find better way to fix problem.

8

So if you have a class A and you somehow run into this problem when injecting the class A into the constructor of class B, use the @Lazy annotation in the constructor of class B. This will break the cycle and inject the bean of A lazily into B. So, instead of fully initializing the bean, it will create a proxy to inject into the other bean. The injected bean will only be fully created when it's first needed.

@Component
public class CircularDependencyA {

    private CircularDependencyB circB;

    @Autowired
    public CircularDependencyA(@Lazy CircularDependencyB circB) {
        this.circB = circB;
    }
}
Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
Dancan Chibole
  • 111
  • 1
  • 3
1

Instead of using Constructor Injection just use the @Autowired annotation. and also add the following line in resources/application.properties file

spring.main.allow-circular-references=true

For example :

Example for using @Autowired

Arjun Gautam
  • 321
  • 2
  • 5
1

Have you upgraded your Spring Boot to 2.6.0 and later? Maybe you should modify your SecurityConfiguration. See this

In my project, I did this. Finially, it works well.

import com.yourweb.filter.JwtAuthenticationTokenFilter;
import com.yourweb.security.AuthenticationEntryPointImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@EnableWebSecurity(debug = true)
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {

    @Resource
    private AuthenticationEntryPointImpl authenticationEntryPoint;

    @Resource
    private LogoutSuccessHandler logoutSuccessHandler;

    @Resource
    private JwtAuthenticationTokenFilter authenticationTokenFilter;

    @Bean
    public AuthenticationManager authManager(
            HttpSecurity http,
            UserDetailsService userDetailsService,
            PasswordEncoder passwordEncoder,
            UserDetailsPasswordService userDetailsPasswordService) throws Exception {
        return http.getSharedObject(AuthenticationManagerBuilder.class)
                .userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder)
                .userDetailsPasswordManager(userDetailsPasswordService)
                .and()
                .build();
    }

    

    @Bean
    public PasswordEncoder passwordEncoder() {
        String idForEncode = "bcrypt";
        Map<String, PasswordEncoder> encoders = new HashMap<>(15);
        encoders.put(idForEncode, new BCryptPasswordEncoder());
        return new DelegatingPasswordEncoder(idForEncode, encoders);
    }

    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .csrf().disable()
                .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                .antMatchers("/auth/login", "/captcha").anonymous()
                .antMatchers(
                        HttpMethod.GET,
                        "/*.html",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js"
                ).permitAll()
                .antMatchers("/profile/**").anonymous()
                .antMatchers("/upload/**").anonymous()
                .antMatchers("/common/download**").anonymous()
                .antMatchers("/swagger-ui/**").anonymous()
                .antMatchers("/swagger-resources/**").anonymous()
                .antMatchers("/webjars/**").anonymous()
                .antMatchers("/*/api-docs").anonymous()
                .antMatchers("/druid/**").anonymous()
                .antMatchers("/modeler/**").anonymous()
                .antMatchers("/process/general/read-resource/**").anonymous()
                .antMatchers("/process/definition/resource/**").anonymous()
                .antMatchers("/activiti/getTracePhoto/**").anonymous()
                .antMatchers("/process/getTracePhoto/**").anonymous()
                .antMatchers("/**/deviceFileMaintenance/addDeviceFileMaintenance").anonymous()
                .antMatchers("/**/deviceFileInstall/addDeviceFileInstall").anonymous()
                .antMatchers("/**/photoUpload").anonymous()
                .anyRequest().authenticated()
                .and()
                .headers().frameOptions().disable()
                .and()
                .logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler)
                .and()
                .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }
}
Junheng
  • 91
  • 1
  • 3
0

I've just had the same issue with the code:

@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration {

    private final AuthenticationProvider authenticationProvider;
    private final JwtAuthenticationFilter jwtAuthenticationFilter;

    public WebSecurityConfiguration(
            AuthenticationProvider authenticationProvider,
            JwtAuthenticationFilter jwtAuthenticationFilter
    ) {
        this.authenticationProvider = authenticationProvider;
        this.jwtAuthenticationFilter = jwtAuthenticationFilter;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeHttpRequests()
                .requestMatchers(HttpMethod.POST, "/api/v1/users")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authenticationProvider(authenticationProvider)
                .addFilterBefore(
                        jwtAuthenticationFilter,
                        UsernamePasswordAuthenticationFilter.class
                );

        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManager(
            AuthenticationConfiguration configuration
    ) throws Exception {
        return configuration.getAuthenticationManager();
    }

    @Bean
    public AuthenticationProvider authenticationProvider(
            UserDetailsService userDetailsService,
            PasswordEncoder passwordEncoder
    ) {
        var daoAuthenticationProvider = new DaoAuthenticationProvider();

        daoAuthenticationProvider.setUserDetailsService(userDetailsService);
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);

        return daoAuthenticationProvider;
    }

}

I then extracted the last three beans to a new class, with @Configuration annotation. It did the trick.

hazartilirot
  • 195
  • 1
  • 2
  • 11
-1

just add in your propriety .yml

spring: main: allow-circular-references: true

-1

I got out the method inside one of the classes which is causing the ciruclar dependency into another new class and resolve in such way.