Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
无法获取使用Spring引导和基于Java的配置注入的UserDetailsManager_Java_Spring_Spring Security_Autowired - Fatal编程技术网

无法获取使用Spring引导和基于Java的配置注入的UserDetailsManager

无法获取使用Spring引导和基于Java的配置注入的UserDetailsManager,java,spring,spring-security,autowired,Java,Spring,Spring Security,Autowired,我有spring boot webapp,它使用基于Java的配置来配置JdbcUserDetailsManager: @Configuration @EnableWebMvcSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired protected DataSource dataSource; @Autowired public void c

我有spring boot webapp,它使用基于Java的配置来配置JdbcUserDetailsManager:

@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

@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: {}
但我知道,在调用
应用程序
构造函数之前,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());
                });
    }

}

向普利尼奥·潘塔莱昂致敬,因为他把我推向了正确的方向。不幸的是,我不能就悬赏一事发表评论。我还不清楚为什么
AuthenticationManagerBuilder
没有在上下文中将userdetails服务自动注册为Bean。如果有人能提供一个权威性的答案,说明为什么我必须提供一个getter,或者解释如何在没有getter的情况下让它工作(这让我觉得有点不舒服),我会为这个答案奖励奖金。

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名称

补充一些细节:

首先,调用
jdbccuthentication()
会产生一个新的
jdbccuserdetailsmanager
实例,但它完全是内部的(即,不是Spring管理的bean)。我们可以说,因为Spring会抱怨当有多个bean满足一次注射时。有关详细信息,请查看的源代码以及各种超类。基本上,您将看到,
jdbccuthentication()
调用会产生一个内部详细信息管理器,对
userDetailsService()的调用将取代它

其次,调用
userDetailsService()
将丢弃
jdbc身份验证()
配置。以下是来自
AuthenticationManagerBuilder
的相关方法:

public <T extends UserDetailsService>
        DaoAuthenticationConfigurer<AuthenticationManagerBuilder,T>
        userDetailsService(T userDetailsService) throws Exception {

    this.defaultUserDetailsService = userDetailsService;
    return apply(
        new DaoAuthenticationConfigurer<AuthenticationManagerBuilder,T>
        (userDetailsService));
}
公共
DaoAuthenticationConfigurer
userDetailsService(T userDetailsService)引发异常{
this.defaultUserDetailsService=userDetailsService;
退货申请(
新的DaoAuthenticationConfigurer
(用户详细信息服务);
}
这就是为什么我们将
JdbcUserDetailsManager
配置从
jdbcAuthentication()。(调用
jdbcAuthentication()
基本上为创建
JdbcUserDetailsManager
提供了一个方便、流畅的界面,但是我们这里不需要它,因为我们已经有了
JdbcUserDetailsManager

现在有了更好的方法是重写
websecurityConfigureAdapter.userDetailsServiceBean()
并将其注册为
@Bean

public static class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Bean(name = "myUserDetailsService")
  // any or no name specified is allowed
  @Override
  public UserDetailsService userDetailsServiceBean() throws Exception {
    return super.userDetailsServiceBean();
  }

}
Javadoc:

重写此方法以将从configure(AuthenticationManagerBuilder)创建的UserDetails服务作为bean公开。通常,此方法仅应执行以下覆盖:

见上面的例子

要更改返回的实例,开发人员应该改为更改userDetailsService()


websecurityConfigureAdapter.configure(AuthenticationManagerBuilder)的Javadoc中也提到了此方法

如果我的问题很愚蠢,我提前向您道歉,但您不需要在configAuthentication方法上构造UserDetailsManager并将其作为bean返回吗?我如何确定AuthenticationManagerBuilder.jdbAuthentication()不会实例化另一个JdbcUserDetailsManager?感谢您的澄清。但这是否意味着在jdbcAuthentication()之后链接的配置将被丢弃?在这种情况下有必要调用jdbcAuthentication吗?简单多了…:)