I've created a spring-mvc application. the configurations look like below:
dispatcher-servlet.xml
<beans ... >
<mvc:annotation-driven />
<context:annotation-config />
<aop:aspectj-autoproxy/>
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>
<context:component-scan base-package="my.server.controller" />
<context:component-scan base-package="my.server.dao" />
<context:component-scan base-package="my.server.service" />
......
</beans>
applicationContext.xml
<beans ...>
...
<bean id="myUserDetailsService" class="my.server.service.MyUserDetailsService" autowire="byType"/>
</beans>
security.xml
<beans:beans ...>
<http pattern="/css/**" security="none"/>
<http pattern="/login.jsp*" security="none"/>
<http auto-config='true'>
<http-basic />
<logout />
<intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**" access="ROLE_USER" />
</http>
<authentication-manager>
<authentication-provider user-service-ref="myUserDetailsService" >
<password-encoder hash="bcrypt" />
</authentication-provider>
</authentication-manager>
</beans:beans>
security.xml and applicationContext.xml are loaded by the following lines in the web.xml
:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml, /WEB-INF/security.xml</param-value>
</context-param>
my.server.dao.UserDao.java
@Component
public interface UserDao {
JUser findByUserName(String username);
}
my.server.dao.UserDaoMemory.java
@Component("userDao")
public class UserDaoMemory implements UserDao {
private static List<JUser> myUsers = new ArrayList<>();
static {
myUsers.add(new JUser("ali", "123","ROLE_USER"));
}
@SuppressWarnings("unchecked")
@Override
public JUser findByUserName(String username) {
List<JUser> users = new ArrayList<>();
users = myUsers;
if (users.size() > 0) {
return users.get(0);
} else {
return null;
}
}
}
my.server.service.MyUserDetailsService.java
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(final String throws UsernameNotFoundException {
System.out.println("MyUserDetailsService#loadUserByUsername, userDao:"+userDao);
JUser user = userDao.findByUserName(username);
List<GrantedAuthority> authorities = buildUserAuthority(user.getUserRoles());
return buildUserForAuthentication(user, authorities);
}
// Converts my.server.entity.JUser user to
// org.springframework.security.core.userdetails.User
private User buildUserForAuthentication(JUser user,
List<GrantedAuthority> authorities) {
return new User(user.getUsername(),
user.getPassword(), user.isEnabled(),
true, true, true, authorities);
}
private List<GrantedAuthority> buildUserAuthority(Set<JRole> userRoles) {
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
// Build user's authorities
for (JRole userRole : userRoles) {
setAuths.add(new SimpleGrantedAuthority(userRole.getRole()));
}
List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);
return Result;
}
public UserDao getUserDao() {
return userDao;
}
}
My problem is in MyUserDetailsServie
where the loadUserByUsername(..)
be called; The userDao
has not been autowired and is null
:
MyUserDetailsService#loadUserByUsername, userDao:null
One probable solution is to change the applicationContext.xml
such as(autowire="byName"
):
<bean id="myUserDetailsService" class="my.server.service.MyUserDetailsService" autowire="byName"/>
But it is not working!!!!
You have some conflicts in how your context are set up.
The applicationContext.xml (the parent context) would generally be where you define all your shared components (e.g. repositories, security, services, handlers, ...), which you have done correctly.
Your servlet context (e.g. dispatcher-servlet.xml) is a child context, and would have visibility to everything defined in the parent context. So you should limit the scope of the components to just controllers for the servlet context, which means you should only scan for your controllers, but you're scanning everything.
<context:component-scan base-package="my.server.controller" />
<context:component-scan base-package="my.server.dao" />
<context:component-scan base-package="my.server.service" />
it should just be
<context:component-scan base-package="my.server.controller" />
you can move all of these to your applicationContext as well
<mvc:annotation-driven />
<context:annotation-config />
<aop:aspectj-autoproxy/>
unless you're using them specifically for this servlet context.
Anyways, getting back to the problem, since child context is scanning for the service/dao classes, it will find those, but they're not properly configured.
so where do I scan my.server.dao
Put them into the applicationContext.xml for now, and into something like a repositoryContext in the future as your application complexity grows.
Technically you can dump everything into a servlet context if you wanted to, but any time you define a "root" context in addition to a servlet context, you'll create scoping issue. Everything in a child context (e.g. servlet context) can see components in a parent context (e.g anything loaded by your web.xml contextloader), but not the reverse.