我目前正在开发一款Vaadin spring应用,根据应用规范,用户的认证授权必须通过jdbcTemplate
查询数据库来完成,这个问题怎么解决?我使用的是Sping Boot 1.4.2.RELEASE。
UPDATE:此方法适用于Sping 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);
}
JdbcAccount存储库:
@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
);
}
}
Jdbc用户详细信息服务:
@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();
}
}
}
如果将Sping Boot 版本降级到[1.1.5,1.2.0),这个问题就不会发生(由于其他依赖关系,我必须使用最新版本)
8条答案
按热度按时间nhn9ugyo1#
您可以将基于构造函数的依赖注入替换为基于设置函数的依赖注入来解决循环问题,请参阅Spring Framework Reference Documentation:
循环依赖项
如果您主要使用构造函数注入,则有可能创建无法解析的循环依赖场景。
例如:类A通过构造函数注入需要类B的示例,类B通过构造函数注入需要类A的示例,如果你配置类A和B的bean互相注入,Spring IoC容器在运行时检测到这个循环引用,并抛出
BeanCurrentlyInCreationException
。一个可能的解决方案是编辑某些类的源代码,使其由setter而不是构造函数来配置。或者,避免构造函数注入,而只使用setter注入。换句话说,尽管不建议使用setter注入,但您可以使用setter注入来配置循环依赖项。
与典型情况(没有循环依赖关系)不同,bean A和bean B之间的循环依赖关系强制其中一个bean在完全初始化自身之前注入到另一个bean中(典型的先有鸡还是先有蛋的场景)。
qnzebej02#
我更喜欢
@Lazy
方法。这样我就可以坚持一种模式。参见http://www.baeldung.com/circular-dependencies-in-spring
oug3syen3#
您的
PasswordEncoder
bean定义在WebSecurityConfig
中,而WebSecurityConfig
需要JdbcUserDetailsServices
。JdbcUserDetailsServices
又依赖于JdbcAccountRepository
,而JdbcAccountRepository
需要PasswordEncoder
。因此,循环形成。一个简单的解决方案是将PasswordEncoder
bean定义从WebSecurityConfig
中取出。即使在SecurityConfiguration
类内部也将解决循环问题。kb5ga3dv4#
我在一个类的构造函数中使用了
@Lazy
,它解决了我的问题:}
piv4azn75#
从Zeeshan回答:
您的PasswordEncoder bean定义位于需要JdbcUserDetailsServices的WebSecurityConfig中。JdbcUserDetailsServices又依赖于需要PasswordEncoder的JdbcAccountRepository。因此形成了循环。一个简单的解决方案是将PasswordEncoder bean定义从WebSecurityConfig中取出。即使在SecurityConfiguration类内部也将解决循环问题。
另一个简单的建议是将PasswordEncoder定义从public only更改为public static:
收件人:
rekjcdws6#
@Zeeshan Adnan是对的,从
WebSecurityConfig
中取出PasswordEncoder
解决了循环依赖问题。shyt4zoc7#
我为PasswordEncoder创建了一个单独的配置类,然后将其导入securityconfig类,它解决了我的问题。源代码在这里与Spring Boot 最新-〉https://github.com/juyelhushen/JWT-springboot-3-Latest.git
yyhrrdl88#
解决方法之一是不使用构造函数。例如:
您可以用途: