java 使用HttpSecurityConfiguration实现Spring循环依赖

3z6pesqy  于 2023-04-10  发布在  Java
关注(0)|答案(1)|浏览(191)

我遇到了一个循环依赖错误问题。当我在securityConfig类中定义UserDetailsService bean时,会抛出这个错误,但是如果我在authenticationConfig类中定义了同一个bean,错误就消失了,我不知道为什么。这是代码。

@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);
    }
}

这是日志:

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?

然后是这个图:

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]
└─────┘

如果我将UserDetailsService bean移动到另一个类,一切都工作得很好,但我试图理解循环依赖性来自何处。日志显示authManager依赖于HttpSecurity,这是正确的,但HttpSecurity依赖于securityConfig,我试图用调试器检查HttpSecurity bean,但找不到任何依赖性

lf5gs5x2

lf5gs5x21#

我检查了UserDetailsServiceHttpSecurity没有任何直接关系。但是你必须使用UserDetailsService来定义HttpSecurity中的角色。你在Spring API HttpSecurity中有信息。
循环依赖应该与AuthenticationManagerHttpSecurity相关。在旧版spring中,您创建了AuthenticationManager,覆盖了WebSecurityConfigurerAdapter。代码如下:

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

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

但是在最后的版本中,你可以使用filterChain,在你的例子中是securityFilerChange,来示例化AuthenticationManager。bean可能是这样的:

@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();
}

在这个实现中,您只有一个站点使用HttpSecurity,循环依赖错误应该消失。
您可以查看有关如何使用此AuthenticationManager的更多信息,在此link中了解如何升级WebSecurityConfigurerAdapter

相关问题