Reputation: 633
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
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;
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.
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
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