Reb*_*orn 23 java spring spring-security spring-jdbc vaadin
我正在研究Vaadin spring应用程序.我唯一能说的是,用户的身份验证/授权必须通过查询数据库来完成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)
???????
Run Code Online (Sandbox Code Playgroud)
原始代码如下所示:
AccountRepository:
public interface AccountRepository {
void createAccount(Account user) throws UsernameAlreadyInUseException;
Account findAccountByUsername(String username);
}
Run Code Online (Sandbox Code Playgroud)
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
);
}
}
Run Code Online (Sandbox Code Playgroud)
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.");
}
}
}
Run Code Online (Sandbox Code Playgroud)
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
PS如果将Spring Boot版本降级为[1.1.5,1.2.0],则不会出现此问题(由于其他依赖性,我必须使用最新版本)
dur*_*dur 32
你可以取代基于构造函数的依赖注射用基于setter方法的依赖注射来解决周期,看到Spring框架参考文档:
循环依赖
如果您主要使用构造函数注入,则可以创建无法解析的循环依赖关系场景.
例如:类A通过构造函数注入需要类B的实例,而类B通过构造函数注入需要类A的实例.如果将A类和B类的bean配置为相互注入,则Spring IoC容器会在运行时检测到此循环引用,并抛出a
BeanCurrentlyInCreationException.一种可能的解决方案是编辑由setter而不是构造函数配置的某些类的源代码.或者,避免构造函数注入并仅使用setter注入.换句话说,尽管不推荐使用,但您可以使用setter注入配置循环依赖项.
与典型情况(没有循环依赖)不同,bean A和bean B之间的循环依赖强制其中一个bean在完全初始化之前被注入另一个bean(一个经典的鸡/蛋场景).
Jac*_*rts 23
我更喜欢@Lazy方法.这样我就可以坚持一种模式.
请参阅http://www.baeldung.com/circular-dependencies-in-spring
小智 9
您的PasswordEncoderbean 定义在WebSecurityConfig其中需要JdbcUserDetailsServices。JdbcUserDetailsServices再次取决于JdbcAccountRepository哪些需求PasswordEncoder。于是循环就形成了。一个简单的解决方案是PasswordEncoder从WebSecurityConfig. 甚至在SecurityConfiguration类里面也会解决循环问题。
来自Zeeshan的回答:
您的PasswordEncoder bean 定义位于需要JdbcUserDetailsServices 的WebSecurityConfig 中。JdbcUserDetailsServices 再次依赖于需要PasswordEncoder 的JdbcAccountRepository。这样循环就形成了。一个简单的解决方案是从 WebSecurityConfig 中取出 PasswordEncoder bean 定义。即使在 SecurityConfiguration 类内部也会解决循环问题。
另一个简单的建议是将 PasswordEncoder 定义从仅公共更改为公共静态:
@Bean(name = "passwordEncoder")
public PasswordEncoder passwordencoder() {
return new CustomPasswordEncoder();
}
Run Code Online (Sandbox Code Playgroud)
到:
@Bean(name = "passwordEncoder")
public static PasswordEncoder passwordencoder() {
return new CustomPasswordEncoder();
}
Run Code Online (Sandbox Code Playgroud)
小智 7
我@Lazy在一个类的构造函数中使用它解决了我的问题:
public class AService {
private BService b;
public ApplicantService(@NonNull @Lazy BService b) {
this.b = b;
}
}
public class BService {
private AService a;
public ApplicantService(@NonNull BService a) {
this.a = a;
}
Run Code Online (Sandbox Code Playgroud)
}
| 归档时间: |
|
| 查看次数: |
34117 次 |
| 最近记录: |