Spring Security:应用程序上下文中某些bean的依赖关系构成了一个循环

时间:2018-01-21 20:45:02

标签: java spring spring-mvc spring-security circular-dependency

我想为项目配置Spring Security。

然而,它失败并出现下一个错误:

***************************
APPLICATION FAILED TO START
***************************    
Description:    
The dependencies of some of the beans in the application context form a cycle:    
┌─────┐
|  securityConfiguration defined in file [C:\Users\Nazar\Projects\IdeaProjects\Lohika_Projects\blog\target\classes\net\lelyak\edu\configuration\SecurityConfiguration.class]
↑     ↓
|  blogDTS defined in file [C:\Users\Nazar\Projects\IdeaProjects\Lohika_Projects\blog\target\classes\net\lelyak\edu\rest\service\impl\UserDetailsServiceImpl.class]
↑     ↓
|  userServiceImpl defined in file [C:\Users\Nazar\Projects\IdeaProjects\Lohika_Projects\blog\target\classes\net\lelyak\edu\rest\service\impl\UserServiceImpl.class]
└─────┘

接下来,您可以看到我的弹簧安全配置:

@Configuration
@AllArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    private DataSource dataSource;
    @Qualifier("blogDTS")
    private UserDetailsService userDetailsService;
    private AccessDeniedHandler accessDeniedHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/", "/registration", "/403", "/500").permitAll()
                .antMatchers("/posts/**").hasAnyRole("USER")
                .antMatchers("/post/**").hasAnyRole("USER")
                .anyRequest().authenticated()
                .and()
                .formLogin().loginPage("/login").permitAll()
                .usernameParameter("username")
                .passwordParameter("password")
                .defaultSuccessUrl("/posts")
                .and()
                .rememberMe().tokenValiditySeconds(10_000).tokenRepository(persistentTokenRepository())
                .and()
                .logout().permitAll()
                .and()
                .exceptionHandling().accessDeniedHandler(accessDeniedHandler).accessDeniedPage("/403");
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

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

    @Override
    protected UserDetailsService userDetailsService() {
        return userDetailsService;
    }

    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl db = new JdbcTokenRepositoryImpl();
        db.setDataSource(dataSource);
        return db;
    }

    @Bean
    public SavedRequestAwareAuthenticationSuccessHandler
    savedRequestAwareAuthenticationSuccessHandler() {
        SavedRequestAwareAuthenticationSuccessHandler auth
                = new SavedRequestAwareAuthenticationSuccessHandler();
        auth.setTargetUrlParameter("targetUrl");
        return auth;
    }
}

UserDetailsServiceImpl代码段:

@Service("blogDTS")
@AllArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
    private UserServiceImpl userService;

    @Override
    public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {

        BlogUser user = userService.getUser(username);
        List<GrantedAuthority> authorities = buildUserAuthority(user.getRole());

        return buildUserForAuthentication(user, authorities);

    }

    private org.springframework.security.core.userdetails.User buildUserForAuthentication(BlogUser user,
                                                                                          List<GrantedAuthority> authorities) {
        return new org.springframework.security.core.userdetails.User(
                user.getUserName(),
                user.getPassword(),
                user.isEnabled(),
                true,
                true,
                true,
                authorities);
    }

    private List<GrantedAuthority> buildUserAuthority(Role userRole) {
        Set<GrantedAuthority> setAuths = Stream.of(new SimpleGrantedAuthority(userRole.name()))
                .collect(Collectors.toSet());
        return Lists.newArrayList(setAuths);
    }
}

UserServiceImpl是管理存储库操作的常用服务层:

@Service
@AllArgsConstructor
public class UserServiceImpl implements UserService {
    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;

    @Override
    public List<BlogUser> getAllUsers() {
        List<BlogUser> result = new ArrayList<>();
        result.addAll(userRepository.findAll());
        return result;
    }

    @Override
    public BlogUser getUser(String userName) {
        Assert.hasText(userName, "User name is empty");
        return userRepository.findByUserName(userName)
                .orElseThrow(() -> new NotPresentedInDbException(userName));
    }

我无法理解如何使用Spring Security解决这个循环问题。

如何解决此问题?

1 个答案:

答案 0 :(得分:0)

最后,我找到了解决方案,重新设计了我的代码:

@Configuration
@AllArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    private UserDetailsServiceImpl userDetailsService;
    private AccessDeniedHandler accessDeniedHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/", "/registration").permitAll()
                .antMatchers("/posts/**", "/post/*").authenticated()
                .anyRequest().authenticated()
                .and()
                .formLogin().loginPage("/login").permitAll()
                .usernameParameter("username")
                .passwordParameter("password")
                .defaultSuccessUrl("/posts")
                .and()
                .logout().permitAll()
                .and()
                .exceptionHandling().accessDeniedHandler(accessDeniedHandler).accessDeniedPage("/403");
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider());
    }

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService);
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }

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

    @Override
    protected UserDetailsService userDetailsService() {
        return userDetailsService;
    } 
}

简化一点UserDetailsServiceImpl。此外,使用存储库类从DB获取用户:

@Service
@AllArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {

        BlogUser user = userRepository.findByUserName(username)
                .orElseThrow(NotPresentedInDbException::new);

        return buildUserForAuthentication(user);
    }

    private org.springframework.security.core.userdetails.User buildUserForAuthentication(BlogUser user) {

        SimpleGrantedAuthority grantedAuthority = new SimpleGrantedAuthority(user.getRole().name());
        List<GrantedAuthority> authorities = Lists.newArrayList(grantedAuthority);

        return new org.springframework.security.core.userdetails.User(
                user.getUserName(),
                user.getPassword(),
                user.isEnabled(),
                true,
                true,
                true,
                authorities);
    }
}

现在它的工作正常。