Pawel Os.
Pawel Os.

Reputation: 633

Spring: autowired DAO is null in custom UserServiceDetails implementation

I am configuring the security in my Spring project and unfortunately I encountered a problem. Why is the autowired userDAO in CustomUserDetailsService.java null?

Here is the code in question:

CustomUserDetailsService.java

@Service("customUserDetailsService")
@ComponentScan(basePackages="...")
public class CustomUserDetailsService implements UserDetailsService {

   @Autowired
   private UserDAO userDAO;

   public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

       // userDAO is null
       UserModel user = userDAO.getUserByUsername(username);
       ...
   }
}

spring-security.xml

<beans:beans xmlns="http://www.springframework.org/schema/security"
   xmlns:beans="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/security
   http://www.springframework.org/schema/security/spring-security-3.2.xsd">

<http pattern="/resources/**" security="none"/>
<http pattern="/login" security="none"/>

<http auto-config="true">
    <intercept-url pattern="/**" access="ROLE_USER" />
    <form-login
        login-page="/login"
        default-target-url="/home"
        authentication-failure-url="/login?error"
        username-parameter="username"
        password-parameter="password" />
    <logout logout-success-url="/login?logout" />
</http>

<authentication-manager>
    <authentication-provider user-service-ref="customUserDetailsService">
        <password-encoder ref="encoder" />
    </authentication-provider>
</authentication-manager>

<beans:bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
    <beans:constructor-arg name="strength" value="10" />
</beans:bean>

<beans:bean id="customUserDetailsService" class="... .service.CustomUserDetailsService" />

Edit:

UserDAOImpl.java

public class UserDAOImpl implements UserDAO {

private JdbcTemplate jdbcTemplate;

public UserDAOImpl(DataSource dataSource) {
    jdbcTemplate = new JdbcTemplate(dataSource);
}

   public UserModel getUserByUsername(String username) {

       String sql = "SELECT * FROM users WHERE username = '" + username + "'";

       return jdbcTemplate.query(sql, new ResultSetExtractor<UserModel>() {

            public UserModel extractData(ResultSet rs) throws SQLException, DataAccessException {
               if (rs.next()) {
                   UserModel user = new UserModel();
                   user.setUsername(rs.getString("username"));
                   user.setPassword(rs.getString("passwort"));
                   user.setEnabled(rs.getBoolean("enabled"));

                   return user;
               }
               return null;
           }
       });
   }
}

Here is the code snippet in the Java-based configuration file:

@Bean
public UserDAO getUserDAO() {
    return new UserDAOImpl(getDataSource());
}

Interesting though, this configuration seems to be sort of working. When I autowire the UserDAO into my controller, it works perfectly fine:

MainController.java

@RequestMapping(value = {"", "/", "/home"})
public String homeHandler(Model model) {

    // userDAO is not null, works perfectly fine and returns the UserModel-Object as expected
    UserModel user = userDAO.getUserByUsername("dx");

    ...
}

I already read some Q&A, yet so far it concerned manual instatiation of the custom UserDetailsService. What is wrong with my code?

Upvotes: 0

Views: 4314

Answers (2)

madteapot
madteapot

Reputation: 2274

The issue seems to be because you are instantiating the customUserDetailsService in two different places. You have <bean> definition for it in your spring-security.xml file as well as you have @Service annotation on the class itself. In your XML bean definition, you are not injecting the UserDAO so that it is null.

You need to streamline your project to clean the bean definition in either java config or XML files (this is not required and you can have XML + java config but then it makes it very confusing as to what is instantiating which bean). You have few options;

  1. Remove the XML declaration of CustomUserDetailsService. Add <context:component-scan> in your XML and add the package names and also remove the @ComponentScan from CustomUserDetailsService. This will allow spring to scan the packages and register the beans marked with @Service, @Bean, etc annotations. Make sure your java config class is marked with @Configuration annotation.

  2. You can Decide to use XML config for all the beans in which case you need to remove the @Bean, @Service, etc annotations and declare all of them in the spring XML bean definition and make sure each of them has proper dependencies injected.

You definitely need to clean up your bean definitions so correct dependencies are injected.

Upvotes: 1

Pawel Os.
Pawel Os.

Reputation: 633

Thank you for your responses. Setu's answer was the most helpful one. Obviously, the problem was indeed the double instantiation. I added <context:component-scan base-package="... .service" /> and left both annotations @Service("customUserDetailsService") and @ComponentScan(basePackages="...") in CustomUserDetailsService (without @ComponentScan it didn't work).

Upvotes: 0

Related Questions