启动应用程序时Spring Security中的循环引用错误

问题描述 投票:0回答:1

我正在尝试学习 Spring Security。我已经理解了一点,但我遇到了一个似乎无法解决的循环错误。我还将与您分享课程的代码。

Springboot控制台日志;

Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2024-08-13T22:39:16.780+03:00 ERROR 1180 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

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

Description:

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

┌─────┐
|  accountAuthenticationProvider defined in file [C:\Users\shiden\App\Project\Java_Web_Backend\38 Springboot Security WebSecurtiyConfig\target\classes\com\bb\proje\config\AccountAuthenticationProvider.class]
↑     ↓
|  accountDetailsUserServiceImp defined in file [C:\Users\shiden\App\Project\Java_Web_Backend\38 Springboot Security WebSecurtiyConfig\target\classes\com\bb\proje\serviceImpl\AccountDetailsUserServiceImp.class]
↑     ↓
|  accountSecurityConfig defined in file [C:\Users\shiden\App\Project\Java_Web_Backend\38 Springboot Security WebSecurtiyConfig\target\classes\com\bb\proje\config\AccountSecurityConfig.class]
└─────┘


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.

package com.bb.proje.config;

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.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import lombok.RequiredArgsConstructor;

@EnableWebSecurity
@Configuration
public class AccountSecurityConfig {

    private final AccountAuthenticationProvider accountAuthenticationProvider;
    
    public AccountSecurityConfig(AccountAuthenticationProvider accountAuthenticationProvider) {
        this.accountAuthenticationProvider = accountAuthenticationProvider;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        
        System.out.println(http);
        
        http.csrf().disable()
                    .authorizeHttpRequests()
                        .requestMatchers("/api/account/**").hasRole("ADMIN")
                        .anyRequest().authenticated()
                    .and().authenticationProvider(accountAuthenticationProvider)
                    .httpBasic(); 
                    
          return http.build();
    }
    
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

package com.bb.proje.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import com.bb.proje.serviceImpl.AccountDetailsUserServiceImp;

import lombok.AllArgsConstructor;


@Component
public class AccountAuthenticationProvider implements AuthenticationProvider {

    private final AccountDetailsUserServiceImp accountDetailsUserServiceImp;

    public AccountAuthenticationProvider(AccountDetailsUserServiceImp accountDetailsUserServiceImp) {
            
        this.accountDetailsUserServiceImp = accountDetailsUserServiceImp;
    }

    @Override
    public Authentication authenticate( Authentication authentication) throws AuthenticationException {

        
        
        

        try {

            UserDetails userdaDetails = accountDetailsUserServiceImp.loadUserByUsername(authentication.getName());
            
            return new UsernamePasswordAuthenticationToken(userdaDetails, userdaDetails.getPassword(),
                    userdaDetails.getAuthorities());

            

        } catch (UsernameNotFoundException e) {
            throw new BadCredentialsException("Account Not Found");
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {

        return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
    }

}

package com.bb.proje.serviceImpl;

import java.util.Collection;
import java.util.Optional;
import java.util.stream.Collectors;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.User.UserBuilder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;


import com.bb.proje.entity.Account;
import com.bb.proje.repository.AccountRepository;

import lombok.AllArgsConstructor;

@Component
public class AccountDetailsUserServiceImp implements UserDetailsService {

    private final AccountRepository repository;
    private final PasswordEncoder passwordEncoder;
    
    public AccountDetailsUserServiceImp(AccountRepository repository, PasswordEncoder passwordEncoder) {
        
        this.repository = repository;
        this.passwordEncoder = passwordEncoder;
        
    }
    

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Optional<Account> accountOptional = repository.findByUsername(username);
        UserBuilder accountBuilder;

        if (accountOptional.isPresent()) {
            Account account = accountOptional.get();
            Collection<GrantedAuthority> authorities = account.getRoles().stream()
                    .map(role -> new SimpleGrantedAuthority(role))
                    .collect(Collectors.toList());
            for(var s : authorities) {
                System.err.println(s);
            }
            
            accountBuilder = User.withUsername(username);
                    accountBuilder.password(passwordEncoder.encode(account.getPassword()))
                    .authorities(authorities);
                   
        } else {
            throw new UsernameNotFoundException("Account not found");
        }
        
        return accountBuilder.build();
    }
}

我将 @Lazy 注释添加到 AccountAuthenticationProvider 类,然后我的项目正在运行,但我认为有问题。因为SecurityFilterChain的方法总是给出403。

private final AccountDetailsUserServiceImp accountDetailsUserServiceImp;

    public AccountAuthenticationProvider(AccountDetailsUserServiceImp accountDetailsUserServiceImp) {
            
        this.accountDetailsUserServiceImp = accountDetailsUserServiceImp;
    }

在DB中有一个ADMIN角色的账户。 (字符串对象)

账户类别

package com.bb.proje.entity;

import java.util.Date;

import java.util.List;

import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Account {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String firstname;

    @Column(nullable = false)
    private String lastname;

    @Column(nullable = false)
    private String username;

    @Column(nullable = false)
    private String password;

    @Column(nullable = false, unique = true)
    private String email;

    @Column(nullable = false)
    private List<String> roles;

    private Date createdAccountTime;

}

spring spring-boot spring-security javabeans spring-bean
1个回答
0
投票

添加static修饰符解决了如下循环引用问题

    @Bean
    public static PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
© www.soinside.com 2019 - 2024. All rights reserved.