在现在的应用当中,我们都会把用户信息都存到数据库当中,所以我们要把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