-1

i am having a problem with a circular dependency error. it is thrown when i define a UserDetailsService bean in my securityConfig class, however if i define that same bean in my authenticationConfig class,the error is gone and i dont get why. this is the code.

@Configuration
public class SecurityConfig {

    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;
    @Autowired
    private InitialAuthenticationFilter initialAuthenticationFilter;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {    
        return http.csrf().disable()
                .httpBasic().and()
                .addFilterBefore(initialAuthenticationFilter, BasicAuthenticationFilter.class)
                .addFilterAfter(jwtAuthenticationFilter,BasicAuthenticationFilter.class)
                .authorizeHttpRequests().requestMatchers("/actuator/**").permitAll().and()
                .authorizeHttpRequests().anyRequest().authenticated()
                .and()
                .build();
    }

    @Bean
    public UserDetailsService userDetailsService(){    
        UserDetails user = User.builder()
                .username("user")
                .password("123")
                .roles("USER")
                .build();
        UserDetails admin = User.builder()
                .username("admin")
                .password("123")
                .roles("USER", "ADMIN")
                .build();
        return new InMemoryUserDetailsManager(user, admin);        
    }
}
@Configuration
public class AuthenticationConfig {
    @Autowired
    private OtpAuthenticationProvider otpAuthenticationProvider;
    @Autowired
    private UsernamePasswordAuthenticationProvider usernamePasswordAuthenticationProvider;

    @Bean
    public AuthenticationManager authManager(HttpSecurity http) throws Exception {
        AuthenticationManagerBuilder authenticationManagerBuilder =
                http.getSharedObject(AuthenticationManagerBuilder.class);
        authenticationManagerBuilder.authenticationProvider(usernamePasswordAuthenticationProvider).authenticationProvider(otpAuthenticationProvider);
        return authenticationManagerBuilder.build();
    }
}
@Component
public class InitialAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private AuthenticationManager manager;

    @Value("${jwt.signing.key}")
    private String signingKey;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException { // do filtering
        }
}
// and then i have some providers like this one
@Component
public class UsernamePasswordAuthenticationProvider implements AuthenticationProvider{
     
    @Autowired
    private AuthenticationServerProxy proxy;
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
         String password = String.valueOf(authentication.getCredentials());
         proxy.sendAuth(username, password);
         return new UsernamePasswordAuthenticationToken(username, password);
    }

    @Override
    public boolean supports(Class<?> authentication) {
        // TODO Auto-generated method stub
        return UsernamePasswordAuthentication.class.isAssignableFrom(authentication);
    }
}

this is the log:

Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. 
Message: Error creating bean with name 'securityConfig': Unsatisfied dependency expressed through field 'initialAuthenticationFilter': 
Error creating bean with name 'initialAuthenticationFilter': Unsatisfied dependency expressed through field 'manager': 
Error creating bean with name 'authManager' defined in class path resource [root/AuthenticationConfig.class]: 
Unsatisfied dependency expressed through method 'authManager' parameter 0: 
Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.httpSecurity' 
defined in class path resource [org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.class]: 
Failed to instantiate [org.springframework.security.config.annotation.web.builders.HttpSecurity]: 
Factory method 'httpSecurity' threw exception with message: Error creating bean with name 'securityConfig': 
Requested bean is currently in creation: Is there an unresolvable circular reference?

and then this diagram:

The dependencies of some of the beans in the application context form a cycle:
┌─────┐
|  securityConfig (field private root.filters.InitialAuthenticationFilter root.SecurityConfig.initialAuthenticationFilter)
↑     ↓
|  initialAuthenticationFilter (field private org.springframework.security.authentication.AuthenticationManager root.filters.InitialAuthenticationFilter.manager)
↑     ↓
|  authManager defined in class path resource [root/AuthenticationConfig.class]
↑     ↓
|  org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.httpSecurity defined in class path resource [org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.class]
└─────┘

if i move the UserDetailsService bean to another class, everything works perfect, but i am trying to understand where was the circular dependency coming from. The log says that the authManager depends on the HttpSecurity, which is correct, but then the HttpSecurity depends on securityConfig, i tried inspecting the HttpSecurity bean with the debugger but couldnt find any dependency

  • The security configuration needs the `UserDetailsService`, because it needs to create an `AuthenticationManager`. But your filter also needs the `AuthenticationManager`, your filter is also need to create the `SecurityConfig`, which is needed to create the `AuthenticationManager`. Hence a circulair dependency. – M. Deinum Apr 09 '23 at 09:28
  • @M.Deinum so, the UserDetailsService is required in the InitializeUserDetailsManagerConfigurer class for creating the AuthenticationManager, but my UserDetails service is defined in my SecurityConfig class, which is dependent in the filters, whose at the same time are dependents in the manager. and this manager depends on the HttpSecurity bean that cannot be created because it needs the userDetailsService that is defined in my SecurityConfig class, creating a circular dependency. am i right? – Santino Zanone Apr 09 '23 at 19:33
  • Yes. That is also what the analyze output is showing as the circulair dependency. – M. Deinum Apr 10 '23 at 17:53

1 Answers1

0

I check that the UserDetailsService dose not have any direct relation with the HttpSecurity. But you have to use the UserDetailsService to define roles into the HttpSecurity. You have the information in the Spring API HttpSecurity.

The circular dependency should be related with the HttpSecurity of the AuthenticationManager. In old spring you create the AuthenticationManager overriding the WebSecurityConfigurerAdapter. The code was something like:

@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
    authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
}

But in the last versions, you can use the filterChain, in your case securityFilerChange, to instance the AuthenticationManager. The bean could be something like:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {    
    AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
    authenticationManagerBuilder.authenticationProvider(usernamePasswordAuthenticationProvider).authenticationProvider(otpAuthenticationProvider);
    AuthenticationManager authenticationManager = authenticationManagerBuilder.build();
    return http.csrf().disable()
            .httpBasic().and().{...}
            .anyRequest().authenticated()
            .and()
            .authenticationManager(authenticationManager).{...}
            .build();
}

In this implementation you only have one site where you are using the HttpSecurity and the circular dependency error should desapear.

You can check more information about how to use this AuthenticationManager in this link about how to upgrade the WebSecurityConfigurerAdapter.