使用注释的Spring安全登录数据库:NoSuchBeanDefinitionException

Posted

技术标签:

【中文标题】使用注释的Spring安全登录数据库:NoSuchBeanDefinitionException【英文标题】:Spring security Login database using Annotation :NoSuchBeanDefinitionException 【发布时间】:2015-06-04 12:10:49 【问题描述】:

我正在尝试通过数据库进行 Spring Security 登录。

我的 SecurityConfig 代码:

 @Configuration
 @EnableWebSecurity
 public class SecurityConfig extends WebSecurityConfigurerAdapter 

    @Autowired
    DataSource dataSource;

    @Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception 

  auth.jdbcAuthentication().dataSource(dataSource)
              .passwordEncoder(passwordEncoder())
    .usersByUsernameQuery(
                        "SELECT email as username,password,active_yn FROM users where email=?")
    .authoritiesByUsernameQuery(
        "SELECT users.email as username,user_role.role_code as user_role FROM users inner join user_role on users.user_id=user_role.user_id where users.email=?");
   


@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception 
    auth.inMemoryAuthentication().withUser("uname@gmail.com").password("pass").roles("USER");
    auth.inMemoryAuthentication().withUser("admin@gmail.com").password("pass").roles("ADMIN");
    auth.inMemoryAuthentication().withUser("expert@gmail.com").password("pass").roles("EXPERT");


//.csrf() is optional, enabled by default, if using WebSecurityConfigurerAdapter constructor
@Override
protected void configure(HttpSecurity http) throws Exception 

    http.authorizeRequests()
        .antMatchers("/client/**").access("hasRole('ROLE_USER')")
        .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
        .antMatchers("/expert/**").access("hasRole('ROLE_EXPERT')")
        .and()
            .formLogin().loginPage("/login").failureUrl("/login?error")
                .usernameParameter("username").passwordParameter("password")

        .and()
            .logout().logoutSuccessUrl("/login?logout")
        .and()
            .csrf(); 


    @Bean
public PasswordEncoder passwordEncoder()
    PasswordEncoder encoder = new BCryptPasswordEncoder();
    return encoder;

我的 MainConfig 代码:

 @Configuration
 @ComponentScan("com.package.project")
 @EnableWebMvc
 @EnableTransactionManagement
 @PropertySource("classpath:pro-$env.name.properties")
 @Import(SecurityConfig.class)
 public class MainConfig extends WebMvcConfigurerAdapter 
  @Value("$jdbc.classname")
  private String jdbcClassname;
  @Value("$jdbc.url")
  private String jdbcUrl;
  @Value("$jdbc.username")
  private String jdbcUsername;
  @Value("$jdbc.password")
  private String jdbcPassword;
  //code

@Bean(name = "dataSource")
public BasicDataSource dataSource() 
    BasicDataSource dataSource = new BasicDataSource();
    dataSource.setDriverClassName(jdbcClassname);
    dataSource.setUrl(jdbcUrl);
    dataSource.setUsername(jdbcUsername);
    dataSource.setPassword(jdbcPassword);
    return dataSource;

我的消息属性:

jdbc.classname=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://10.0.1.28:3306/test
jdbc.username=root
jdbc.password=pass

我也添加了 类扩展为 AbstractAnnotationConfigDispatcherServletInitializer。

我的例外:

2015-03-31 13:35:32 WARN AnnotationConfigWebApplicationContext:487 - 上下文初始化期间遇到异常 - 取消刷新尝试 org.springframework.beans.factory.BeanCreationException:创建名为“securityConfig”的bean时出错:注入自动装配的依赖项失败;嵌套异常是 org.springframework.beans.factory.BeanCreationException:无法自动装配字段:javax.sql.DataSource com.project.pro.config.SecurityConfig.dataSource;嵌套异常是 org.springframework.beans.factory.NoSuchBeanDefinitionException:没有为依赖项找到类型为 [javax.sql.DataSource] 的合格 bean:预计至少有 1 个 bean 有资格作为此依赖项的自动装配候选者。依赖注解:@org.springframework.beans.factory.annotation.Autowired(required=true) 引起:org.springframework.beans.factory.BeanCreationException:无法自动装配字段:javax.sql.DataSource com.project.pro.config.SecurityConfig.dataSource;嵌套异常是 org.springframework.beans.factory.NoSuchBeanDefinitionException:没有为依赖项找到类型为 [javax.sql.DataSource] 的合格 bean:预计至少有 1 个 bean 有资格作为此依赖项的自动装配候选者。依赖注解:@org.springframework.beans.factory.annotation.Autowired(required=true) 原因:org.springframework.beans.factory.NoSuchBeanDefinitionException:没有为依赖找到[javax.sql.DataSource]类型的合格bean:预计至少有1个bean有资格作为此依赖的自动装配候选者。依赖注解:@org.springframework.beans.factory.annotation.Autowired(required=true) 2015-03-31 13:35:32 错误 ContextLoader:331 - 上下文初始化失败 org.springframework.beans.factory.BeanCreationException:创建名为“securityConfig”的bean时出错:注入自动装配的依赖项失败;嵌套异常是 org.springframework.beans.factory.BeanCreationException:无法自动装配字段:javax.sql.DataSource com.project.pro.config.SecurityConfig.dataSource;嵌套异常是 org.springframework.beans.factory.NoSuchBeanDefinitionException:没有为依赖项找到类型为 [javax.sql.DataSource] 的合格 bean:预计至少有 1 个 bean 有资格作为此依赖项的自动装配候选者。依赖注解:@org.springframework.beans.factory.annotation.Autowired(required=true)

这是我的 AbstractAnnotationConfigDispatcherServletInitializer 填充:

公共类 ProWebAppInitializer 扩展 AbstractAnnotationConfigDispatcherServletInitializer

@Override
protected Class<?>[] getRootConfigClasses() 
    return new Class[] SecurityConfig.class;


@Override
protected Class<?>[] getServletConfigClasses() 
    return new Class[]  MainConfig.class ;


@Override
protected String[] getServletMappings() 
    return new String[]  "/" ;

【问题讨论】:

能否提供 AbstractAnnotationConfigDispatcherServletInitializer @RobWinch:感谢您的回复,我已添加 AbstractAnnotationConfigDispatcherServletInitializer 文件。 【参考方案1】:

问题是 Spring Security 注册在根 ApplicationContext 中,无法看到子 ApplicationContext 中定义的 bean(即 servletConfigClasses)。

将 BasicDataSource 移动到父 ApplicationContext,它应该可以正常工作。请注意,任何与 Spring MVC 相关的配置(即 EnableWebMvc、Controllers 等)都必须在 servletConfigClasses 中声明以确保它们被拾取。

因此,在您的实例中,BasicDataSource 是在 MainConfig 中定义的,它通过 getServletConfigClasses 公开。这意味着 BasicDataSource 仅可用于通过 getServletConfigClasses 公开的配置。

SecurityConfig 通过 getRootConfigClasses 公开,这意味着它在 getServletConfigClasses 中看不到任何内容。这意味着它看不到 BasicDataSource。

通过 getRootConfigClasses 公开的任何配置都可以通过 getServletConfigClasses 看到。

所以你可以按如下方式更新你的配置:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter 

    @Autowired
    DataSource dataSource;

    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception 

        auth
            .jdbcAuthentication()
                .dataSource(dataSource)
                .passwordEncoder(passwordEncoder())
                .usersByUsernameQuery(
                        "SELECT email as username,password,active_yn FROM users where email=?")
                .authoritiesByUsernameQuery(
        "SELECT users.email as username,user_role.role_code as user_role FROM users inner join user_role on users.user_id=user_role.user_id where users.email=?")
                .and()
            .inMemoryAuthentication()
                .withUser("uname@gmail.com").password("pass").roles("USER").and()
                .withUser("admin@gmail.com").password("pass").roles("ADMIN").and()
                .withUser("expert@gmail.com").password("pass").roles("EXPERT");
       

    @Override
    protected void configure(HttpSecurity http) throws Exception 

        http
            .authorizeRequests()
                .antMatchers("/client/**").access("hasRole('ROLE_USER')")
                .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
                .antMatchers("/expert/**").access("hasRole('ROLE_EXPERT')")
                .and()
            .formLogin()
                .loginPage("/login");


@Bean
public PasswordEncoder passwordEncoder()
    return new BCryptPasswordEncoder();



@Configuration
@ComponentScan("com.package.project")
@EnableTransactionManagement
@PropertySource("classpath:pro-$env.name.properties")
public class MainConfig 
    @Value("$jdbc.classname")
    private String jdbcClassname;
    @Value("$jdbc.url")
    private String jdbcUrl;
    @Value("$jdbc.username")
    private String jdbcUsername;
    @Value("$jdbc.password")
    private String jdbcPassword;
    //code

    @Bean(name = "dataSource")
    public BasicDataSource dataSource() 
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(jdbcClassname);
        dataSource.setUrl(jdbcUrl);
        dataSource.setUsername(jdbcUsername);
        dataSource.setPassword(jdbcPassword);
        return dataSource;
    


@Configuration
@ComponentScan("com.package.project")
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter 
     // ... if you don't override any methods you don't need to extend WebMvcConfigurerAdapter


public class ProWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer 
    @Override
    protected Class<?>[] getRootConfigClasses() 
        return new Class[] SecurityConfig.class, MainConfig.class;
    

    @Override
    protected Class<?>[] getServletConfigClasses() 
        return new Class[]  WebMvcConfig.class ;
    

    @Override
    protected String[] getServletMappings() 
        return new String[]  "/" ;
    

【讨论】:

如果这回答了您的问题,请将我的回答标记为正确

以上是关于使用注释的Spring安全登录数据库:NoSuchBeanDefinitionException的主要内容,如果未能解决你的问题,请参考以下文章

-bash: /usr/librxec/grepconf.sh:Nosuch file or directory

在 Spring 安全中使用拦截 URL

使用 Java 配置和 Spring Security 3.2 的安全方法注释

在控制器方法上使用 @Secured 注释时基于 Spring 安全 JDK 的代理问题

Spring安全重定向到登录并恢复之前输入的表单数据

无法使用 CAS 和 Spring 安全性登录到应用程序