我有使用基于Java的配置来配置JdbcUserDetailsManager的spring boot webapp:
@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
protected DataSource dataSource;
@Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select username as principal, password as credentials, true from users where username = ?")
.authoritiesByUsernameQuery("select username as principal, authority as role from authorities where username = ?")
.rolePrefix("ROLE_");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/**")
.authenticated()
.and()
.formLogin()
.successHandler(
(request, response, authentication) -> {
response.setStatus(HttpStatus.NO_CONTENT.value());
})
.failureHandler(
(request, response, authentication) -> {
response.setStatus(HttpStatus.FORBIDDEN.value());
})
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(
(request, response, authentication) -> {
response.setStatus(HttpStatus.NO_CONTENT.value());
});
}
}
我可以在中设置一个断点configAuthentication()
,因此我知道该方法正在被调用。我现在想JdbcUserDetailsManager
在我的Application类中注入:
@EnableAutoConfiguration
@ComponentScan
public class Application {
private Environment env;
private UserDetailsManager userDetailsManager;
@Autowired
public Application(JdbcTemplate jdbcTemplate, Environment env, UserDetailsManager userDetailsManager) {
this.env = env;
this.userDetailsManager = userDetailsManager;
...
当我尝试启动我的应用程序时,出现以下错误:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'application': Unsatisfied dependency expressed through constructor argument with index 2 of type [org.springframework.security.provisioning.UserDetailsManager]: : No qualifying bean of type [org.springframework.security.provisioning.UserDetailsManager] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.provisioning.UserDetailsManager] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
但是我知道一个事实,即在调用Application
构造函数之前实例化了JdbcUserDetailsManager 。这里发生了什么?如何验证JdbcUserDetailsManager是否实际在上下文中注册?
更新:通过更改SecurityConfig
以下内容,我能够解决问题:
@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
protected DataSource dataSource;
private JdbcUserDetailsManager userDetailsManager;
@Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
this.userDetailsManager = auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery(
"select username,password,enabled from users where username=?")
.authoritiesByUsernameQuery(
"select username, role from user_roles where username=?").getUserDetailsService();
}
@Bean(name = "userDetailsManager")
public JdbcUserDetailsManager getUserDetailsManager() {
return userDetailsManager;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/**")
.authenticated()
.and()
.formLogin()
.successHandler(
(request, response, authentication) -> {
response.setStatus(HttpStatus.NO_CONTENT.value());
})
.failureHandler(
(request, response, authentication) -> {
response.setStatus(HttpStatus.FORBIDDEN.value());
})
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(
(request, response, authentication) -> {
response.setStatus(HttpStatus.NO_CONTENT.value());
});
}
}
前往PlínioPantaleão,向正确的方向推动我。不幸的是,我无法授予Bounty评论。我也不清楚为什么AuthenticationManagerBuilder
不能在上下文中自动将UserDetailsService注册为Bean。如果有人可以提供关于为什么我必须提供吸气剂的权威性答案,或者可以解释为什么没有吸气剂就可以使它工作(这对我来说有点不客气),那么我将奖励该答案。
Spring注入Bean,因此必须在上下文中具有Bean才能进行注入。
但是不要在configAuthentication()
方法中创建bean 。用自己的方法创建它,然后从该configAuthentication()
方法中引用它。像这样:
@Bean
public JdbcUserDetailsManager userDetailsManager() {
JdbcUserDetailsManager manager = new JdbcUserDetailsManager();
manager.setDataSource(dataSource);
manager.setUsersByUsernameQuery(
"select username,password,enabled from users where username=?");
manager.setAuthoritiesByUsernameQuery(
"select username, role from user_roles where username=?");
manager.setRolePrefix("ROLE_");
return manager;
}
@Autowired
public void configAuthentication(AuthenticationManagerBuilder builder)
throws Exception {
builder.userDetailsService(userDetailsManager());
}
现在userDetailsManager()
生成一个配置正确的bean(允许注入),您正在使用它进行身份验证。Spring在这里做了一些魔术,以确保重复调用userDetailsManager()
(或任何其他bean定义)一次又一次返回同一对象,而不是每次都创建新实例。
我将您的方法名称从更改getUserDetailsManager()
为userDetailsManager()
。此方法是bean定义,而不是getter,所以这就是原因。我也从@Bean
注释中删除了该名称,因为Spring在这里自动使用方法名称作为bean名称。
一些补充说明,以填充一些详细信息:
首先,对的调用会jdbcAuthentication()
产生一个新JdbcUserDetailsManager
实例,但它完全是内部实例(即不是Spring管理的Bean)。我们可以说是因为Spring抱怨何时有多个bean满足一次注入。有关详细信息,请查看AuthenticationManagerBuilder,JdbcUserDetailsManagerConfigurer和各种超类的源代码。基本上,您将看到该jdbcAuthentication()
调用导致一个内部详细信息管理器,该调用将userDetailsService()
替换该内部细节管理器。
其次,调用userDetailsService()
将放弃jdbcAuthentication()
配置。这是来自的相关方法AuthenticationManagerBuilder
:
public <T extends UserDetailsService>
DaoAuthenticationConfigurer<AuthenticationManagerBuilder,T>
userDetailsService(T userDetailsService) throws Exception {
this.defaultUserDetailsService = userDetailsService;
return apply(
new DaoAuthenticationConfigurer<AuthenticationManagerBuilder,T>
(userDetailsService));
}
这就是为什么我们将JdbcUserDetailsManager
配置移出jdbcAuthentication()
部分并移至userDetailsManager()
方法本身的原因。(该jdbcAuthentication()
调用基本上公开了一个方便,流畅的接口来创建JdbcUserDetailsManager
,但我们这里不需要它,因为我们已经有了JdbcUserDetailsManager
。)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句