Spring Security循环bean依赖
我正在研究Vaadin spring应用程序。 我唯一可以说的是,用户的身份validation/授权必须通过jdbcTemplate
查询数据库来jdbcTemplate
。 如何解决这个问题? 我正在使用Spring Boot 1.4.2.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) └─────┘
原始代码如下所示:
AccountRepository:
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 ); } }
JdbcUserDetailsServices:
@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."); } } }
SecurityConfiguration:
@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的dependency injection替换基于构造函数的 dependency injection来解决循环,请参阅Spring Framework Reference Documentation :
循环依赖
如果您主要使用构造函数注入,则可以创建无法解析的循环依赖关系场景。
例如:类A通过构造函数注入需要类B的实例,而类B通过构造函数注入需要类A的实例。 如果为A类和B类配置bean以便相互注入,则Spring IoC容器会在运行时检测此循环引用,并抛出
BeanCurrentlyInCreationException
。一种可能的解决方案是编辑由setter而不是构造函数配置的某些类的源代码。 或者,避免构造函数注入并仅使用setter注入。 换句话说,尽管不推荐使用,但您可以使用setter注入配置循环依赖关系。
与典型情况(没有循环依赖)不同,bean A和bean B之间的循环依赖强制其中一个bean在完全初始化之前被注入另一个bean(一个经典的鸡/蛋场景)。
我更喜欢@Lazy方法。 这样我就可以坚持一种模式。
请参阅http://www.baeldung.com/circular-dependencies-in-spring
- Java 9:Eclipse中无法访问模块java.xml.bind
- 查找给定整数的因子
- JAXB使用JAX-WS绑定生成可序列化的类
- java.lang.NoClassDefFoundError:在eclipse maven中
- javaList中的ArrayList最后插入的值?
- java.lang.IllegalStateException:前面的方法调用getMessage(“title”)缺少行为定义
- JNA示例程序java.lang.NoClassDefFoundError
- 正则表达式目录中的文件
- 让subclipse在Ubuntu 64和Indigo上工作 – 加载了不兼容的JavaHL库。 需要1.7.x或更高版本