Spring Security循环bean依赖

我目前正在研究Vaadin春季应用程序。根据应用规范,必须通过查询数据库来完成对用户的身份验证/授权jdbcTemplate。如何解决这个问题?我正在使用Spring

Boot 1.4.2.RELEASE。

:此方法适用于Spring Boot 1.1.x.RELEASE,但是在最新版本中会产生以下错误消息。

Description:

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

┌─────┐

| jdbcAccountRepository defined in file [repositories\JdbcAccountRepository.class]

↑ ↓

| securityConfiguration.WebSecurityConfig (field services.JdbcUserDetailsServicessecurity.SecurityConfiguration$WebSecurityConfig.userDetailsService)

↑ ↓

| jdbcUserDetailsServices (field repositories.JdbcAccountRepository services.JdbcUserDetailsServices.repository)

└─────┘

原始代码如下所示:

帐户存储库:

public interface AccountRepository {

void createAccount(Account user) throws UsernameAlreadyInUseException;

Account findAccountByUsername(String username);

}

JdbcAccountRepository:

@Repository

public class JdbcAccountRepository implements AccountRepository {

private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());

private final JdbcTemplate jdbcTemplate;

private final PasswordEncoder passwordEncoder;

@Autowired

public JdbcAccountRepository(JdbcTemplate jdbcTemplate, PasswordEncoder passwordEncoder) {

this.jdbcTemplate = jdbcTemplate;

this.passwordEncoder = passwordEncoder;

}

@Transactional

@Override

public void createAccount(Account user) throws UsernameAlreadyInUseException {

try {

jdbcTemplate.update(

"insert into Account (firstName, lastName, username, password, role) values (?, ?, ?, ?, ?)",

user.getFirstName(),

user.getLastName(),

user.getUsername(),

passwordEncoder.encode(

user.getPassword()),

user.getRole()

);

} catch (DuplicateKeyException e) {

throw new UsernameAlreadyInUseException(user.getUsername());

}

}

@Override

public Account findAccountByUsername(String username) {

return jdbcTemplate.queryForObject(

"select username, password, firstName, lastName, role from Account where username = ?",

(rs, rowNum) -> new Account(

rs.getString("username"),

rs.getString("password"),

rs.getString("firstName"),

rs.getString("lastName"),

rs.getString("role")),

username

);

}

}

JdbcUserDetailsS​​ervices:

@Service

public class JdbcUserDetailsServices implements UserDetailsService {

private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());

@Autowired

JdbcAccountRepository repository;

@Override

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

try {

Account account = repository.findAccountByUsername(username);

User user = new User(

account.getUsername(),

account.getPassword(),

AuthorityUtils.createAuthorityList(

account.getRole()

)

);

return user;

} catch (DataAccessException e) {

LOGGER.debug("Account not found", e);

throw new UsernameNotFoundException("Username not found.");

}

}

}

安全配置:

@Configuration

@ComponentScan

public class SecurityConfiguration {

@Autowired

ApplicationContext context;

@Autowired

VaadinSecurity security;

@Bean

public PreAuthorizeSpringViewProviderAccessDelegate preAuthorizeSpringViewProviderAccessDelegate() {

return new PreAuthorizeSpringViewProviderAccessDelegate(security, context);

}

@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)

public static class GlobalMethodSecurity extends GlobalMethodSecurityConfiguration {

@Bean

@Override

protected AccessDecisionManager accessDecisionManager() {

return super.accessDecisionManager();

}

}

@Configuration

@EnableWebSecurity

public static class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired

JdbcUserDetailsServices userDetailsService;

@Autowired

DataSource dataSource;

@Bean

public PasswordEncoder passwordEncoder() {

return NoOpPasswordEncoder.getInstance();

}

@Bean

public TextEncryptor textEncryptor() {

return Encryptors.noOpText();

}

/*

* (non-Javadoc)

* @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter

* #configure(org.springframework.security.config.annotation.web.builders.WebSecurity)

*/

@Override

public void configure(WebSecurity web) throws Exception {

//Ignoring static resources

web.ignoring().antMatchers("/VAADIN/**");

}

@Override

protected void configure(AuthenticationManagerBuilder auth)

throws Exception {

auth.userDetailsService(userDetailsService);

}

@Bean(name="authenticationManager")

@Override

public AuthenticationManager authenticationManagerBean() throws Exception {

return super.authenticationManagerBean();

}

/*

* (non-Javadoc)

* @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter

* #configure(org.springframework.security.config.annotation.web.builders.HttpSecurity)

*/

@Override

protected void configure(HttpSecurity http) throws Exception {

http

.exceptionHandling()

.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/"))

.and()

.authorizeRequests()

.antMatchers("/**").permitAll()

.and()

.csrf().disable();

}

}

}

PS如果将Spring Boot版本降级到[1.1.5,1.2.0),则不会发生此问题(由于其他依赖性,我必须使用最新的)

回答:

你可以取代基于构造函数的依赖注射用基于setter方法的依赖注射来解决周期,看到Spring框架参考文档:

如果主要使用构造函数注入,则可能会创建无法解决的循环依赖方案。

例如:A类通过构造函数注入需要B类的实例,而B类通过构造函数注入需要A类的实例。如果您将A类和B类的bean配置为相互注入,则Spring

IoC容器会在运行时检测到此循环引用,并抛出BeanCurrentlyInCreationException

一种可能的解决方案是编辑某些类的源代码,这些类的源代码由设置者而不是构造函数来配置。或者,避免构造函数注入,而仅使用setter注入。换句话说,尽管不建议这样做,但是您可以使用setter注入配置循环依赖关系。

与典型情况(没有循环依赖项)不同,Bean A和Bean

B之间的循环依赖关系迫使其中一个Bean在完全初始化之前被注入另一个Bean(经典的Chicken / egg场景)。

以上是 Spring Security循环bean依赖 的全部内容, 来源链接: utcz.com/qa/426037.html

回到顶部