0
votes

I am trying spring security login through database.

My SecurityConfig code:

 @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("[email protected]").password("pass").roles("USER");
    auth.inMemoryAuthentication().withUser("[email protected]").password("pass").roles("ADMIN");
    auth.inMemoryAuthentication().withUser("[email protected]").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;
}

My MainConfig Code:

 @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;
}

}

My Message property :

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

I have also added class extended to AbstractAnnotationConfigDispatcherServletInitializer.

my Exceptions:

     2015-03-31 13:35:32 WARN  AnnotationConfigWebApplicationContext:487 - Exception encountered during context initialization - cancelling refresh attempt

     org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'securityConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: javax.sql.DataSource com.project.pro.config.SecurityConfig.dataSource; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

   Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: javax.sql.DataSource com.project.pro.config.SecurityConfig.dataSource; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

   Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

     2015-03-31 13:35:32 ERROR ContextLoader:331 - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'securityConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: javax.sql.DataSource com.project.pro.config.SecurityConfig.dataSource; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

Here my AbstractAnnotationConfigDispatcherServletInitializer Fille:

public class ProWebAppInitializer extends 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[] { "/" };
}

}

1
Can you provide AbstractAnnotationConfigDispatcherServletInitializer - Rob Winch
@RobWinch: Thank you for responding, I have added AbstractAnnotationConfigDispatcherServletInitializer file. - Chaitanya Joshi

1 Answers

1
votes

The problem is that Spring Security is registered in the root ApplicationContext which cannot see beans defined in the child ApplicationContext (i.e. servletConfigClasses).

Move the BasicDataSource to the parent ApplicationContext and it should work just fine. Note that any Spring MVC related configuration (i.e. EnableWebMvc, Controllers, etc) must be declared in servletConfigClasses to ensure they are picked up.

So in your instance, BasicDataSource is defined in MainConfig which is exposed via getServletConfigClasses. This means that BasicDataSource is only available to configuration exposed via getServletConfigClasses.

SecurityConfig is exposed via getRootConfigClasses which means it cannot see anything in getServletConfigClasses. This means it cannot see BasicDataSource.

Any configuration exposed via getRootConfigClasses can be seen via getServletConfigClasses.

So you can update your configuration as follows:

@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("[email protected]").password("pass").roles("USER").and()
                .withUser("[email protected]").password("pass").roles("ADMIN").and()
                .withUser("[email protected]").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[] { "/" };
    }
}