在现在的应用当中,我们都会把用户信息都存到数据库当中,所以我们要把security基于内存的操作改为基于数据库的操作。
其实不管基于内存的操作还是基于数据库的操作,security的目的都是只有一个,拿到当前的User以及相关的UserDetails信息,在基于内存的时候,用户名以及相关的UserDetails都会存到内存中,同理,如果迁移到数据库中,那么,我们可以从数据库中查出当前User以及相关的用户信息,然后封装成一个实现了UserDetails接口的User实现类。理解到这里,目标已经非常明确了:返回一个实现了UserDetails的User类给框架。
在security框架中,无论是InMemoryDaoImpl还是基于数据库的实现类都是利用同一个接口UserDetailService的方法loadUserByUsername。(这种面向接口的设计方式是非常棒的,纯属个人的感悟)。这里我们只需要改变这个方法的实现方式就可以达到目的了。
?
到这里,我们已经知道怎么样去把基于内存的操作迁移到数据库中了,下面的实现,只要按照这种思路来做,就不会有什么问题了。
?
首先,我们需要创建一个类实现UserDetailService接口,并且实现方法loadUserByUsername(下面是笔者自己的例子,仅供参考):
package service;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Resource;
import model.Users;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import dao.BaseCollectionDao;
@Service("hibernateUserDetailsService")
public class HibernateUserDetailsService implements UserDetailsService {
private BaseCollectionDao baseCollectionDao;
@Resource(name="baseCollectionDao")
public void setBaseCollectionDao(BaseCollectionDao baseCollectionDao)
{
this.baseCollectionDao = baseCollectionDao;
}
@Transactional
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException
{
Users users = baseCollectionDao.findUniqueByProperty(Users.class, "username", username);
if(users == null)
throw new UsernameNotFoundException("用户" + username + " 不存在!!!");
String[] roles = users.getRoles().split(",");
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
for(String role : roles)
{
authorities.add(new SimpleGrantedAuthority(role));
}
return new User(users.getUsername(), users.getPassword(), authorities);
}
}
?
?从上面的代码可以看到,User是从数据库查找出来的,而基于内存的操作,用户及其用户信息都是放到Map中的。为了方便对比,把InMemoryDaoImpl的实现方法也贴出来:
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userMap.getUser(username);
}
?
从上面两段代码的对比就可以看出,基于内存操作和基于数据库操作的根本区别。
?
接下来,定义好了UserDetailService,那么就应用到security框架中,在之前的篇章中已经说过了怎么配置,这里就简单贴出代码就行了:
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="hibernateUserDetailsService">
</authentication-provider>
</authentication-manager>
?
因为dao层的操作每个人都不同,而这个问题并不重要,最主要的是把UserDetailService的loadUserByUsername的方法用基于数据库查询的方式实现。所以这里就不关注dao层的实现了。其实这里的变化就是把InMemoryDaoImpl类,换成了HibernateUserDetailService。
?
下面贴出本人的从service层到dao层的实现(security登录不需要controller层),以及配置文件(仅供参考):
hibernateUserDetailService:
package service; import java.util.HashSet; import java.util.Set; import javax.annotation.Resource; import model.Users; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframewo
