日期:2014-05-16  浏览次数:20566 次

springsecurity第四章——使用数据库的spring security后台认证

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