spring security 原理

在SpringSide 3的官方文档中,说安全框架使用的是Spring Security 2.0。乍一看,吓了我一跳,觉得Acegi这么快就被淘汰了呢。上搜索引擎一搜,发现原来Spring Security 2.0就是Acegi 2.0。悬着的心放下来了。虽然SpringSide 3中关于Acegi的配置文件看起来很不熟悉,可是读了Acegi 2.0的官方文档后,一切都释然了。

先来谈一谈Acegi的基础知识,Acegi的架构比较复杂,可是我但愿我下面的只言片语可以把它说清楚。你们都知道,若是要对Web资源进行保护,最好的办法莫过于Filter,要想对方法调用进行保护,最好的办法莫过于AOP。Acegi对Web资源的保护,就是靠Filter实现的。以下图:
001.PNG

通常来讲,咱们的Filter都是配置在web.xml中,可是Acegi不同,它在web.xml中配置的只是一个代理,而真正起做用的Filter是做为Bean配置在Spring中的。web.xml中的代理依次调用这些Bean,就实现了对Web资源的保护,同时这些Filter做为Bean被Spring管理,因此实现AOP也很简单,真的是一箭双雕啊。

Acegi中提供的Filter很多,有十多个,一个一个学起来比较复杂。可是对于咱们Web开发者来讲,经常使用的就那么几个,以下图中的被红圈圈标记出来的:
002.PNG

从上到下,它们实现的功能依次是一、制定必须为https链接;二、从Session中提取用户的认证信息;三、退出登陆;四、登陆;五、记住用户;六、全部的应用必须配置这个Filter。

通常来讲,咱们写Web应用只须要熟悉这几个Filter就能够了,若是不须要https链接,连第一个也不用熟悉。可是有人确定会想,这些Filter怎么和个人数据库联系起来呢?不用着急,这些Filter并不直接处理用户的认证,也不直接处理用户的受权,而是把它们交给了认证管理器和决策管理器。以下图:
003.PNGjava

对于这两种管理器,那也是不须要咱们写代码的,Acegi也提供了现成的类。那么你们又奇怪了:又是现成的,那怎么和个人数据库关联起来呢?别着急,其实这两个管理器本身也不作事,认证管理器把任务交给了Provider,而决策管理器则把任务交给了Voter,以下图:
004.PNGweb

如今我要告诉大家,这里的Provider和Voter也是不须要咱们写代码的。不要崩溃,快到目标了。Acegi提供了多个Provider的实现类,若是咱们想用数据库来储存用户的认证数据,那么咱们就选择DaoAuthenticationProvider。对于Voter,咱们通常选择RoleVoter就够用了,它会根据咱们配置文件中的设置来决定是否容许某一个用户访问制定的Web资源。

而DaoAuthenticationProvider也是不直接操做数据库的,它把任务委托给了UserDetailService,以下图:
005.PNG

而咱们要作的,就是实现这个UserDetailService。图画得很差,你们不要见笑,可是说了这么多总算是引出了咱们开发中的关键,那就是咱们要实现本身的UserDetailService,它就是链接咱们的数据库和Acegi的桥梁。UserDetailService的要求也很简单,只须要一个返回org.springframework.security.userdetails.User对象的loadUserByUsername(String userName)方法。所以,怎么设计数据库均可以,无论咱们是用一个表仍是两个表仍是三个表,也无论咱们是用户-受权,仍是用户-角色-受权,仍是用户-用户组-角色-受权,这些具体的东西Acegi通通不关心,它只关心返回的那个User对象,至于怎么从数据库中读取数据,那就是咱们本身的事了。

反过来再看看上面的过程,咱们发现,即便咱们要作的只是实现本身的UserDetailService类,可是咱们不得不在Spring中配置那一大堆的Bean,包括几个Filter,几个Manager,几个Provider和Voter,而这些配置每每都是重复的无谓的。好在Acegi 2.0也认识到了这个问题,因此,它设计了一个<http>标签,让Acegi的配置获得了简化。下面是SpringSide 3中的配置的截图,你们能够看看:spring


006.PNG

下图是官方文章中的传统Filter设置和<http>元素之间的对应关系:
007.PNG

下面的代码是SpringSide 3中实现UserDetailService的范例,在SpringSide 3的范例中,白衣使用了三个表User、Role、Authority。可是Acegi不关心你用了几个表,它只关心UserDetails对象。而决定用户可否访问指定Web资源的,是RoleVoter类,无需任何修改它能够工做得很好,惟一的缺点是它只认ROLE_前缀,因此搞得白衣的Authority看起来都象角色,不三不四。数据库

package  personal.youxia.service.security;

import  java.util.ArrayList;
import  java.util.List;

import  org.springframework.beans.factory.annotation.Required;
import  org.springframework.dao.DataAccessException;
import  org.springframework.security.GrantedAuthority;
import  org.springframework.security.GrantedAuthorityImpl;
import  org.springframework.security.userdetails.UserDetails;
import  org.springframework.security.userdetails.UserDetailsService;
import  org.springframework.security.userdetails.UsernameNotFoundException;
import  personal.youxia.entity.user.Authority;
import  personal.youxia.entity.user.Role;
import  personal.youxia.entity.user.User;
import  personal.youxia.service.user.UserManager;

/** 
 * 实现SpringSecurity的UserDetailsService接口,获取用户Detail信息.
 * 
 *  @author  calvin
  */ 
public   class  UserDetailServiceImpl  implements  UserDetailsService {

     private  UserManager userManager;

     public  UserDetails loadUserByUsername(String userName)  throws  UsernameNotFoundException, DataAccessException {
        User user  =  userManager.getUserByLoginName(userName);
         if  (user  ==   null )
             throw   new  UsernameNotFoundException(userName  +   "  不存在 " );

        List < GrantedAuthority >  authsList  =   new  ArrayList < GrantedAuthority > ();

         for  (Role role : user.getRoles()) {
             for  (Authority authority : role.getAuths()) {
                authsList.add( new  GrantedAuthorityImpl(authority.getName()));
            }
        }

         //  目前在MultiDatabaseExample的User类中没有enabled, accountNonExpired,credentialsNonExpired, accountNonLocked等属性
         //  暂时所有设为true,在须要时才添加这些属性. 
        org.springframework.security.userdetails.User userdetail  =   new  org.springframework.security.userdetails.User(
                user.getLoginName(), user.getPassword(),  true ,  true ,  true ,  true , authsList
                        .toArray( new  GrantedAuthority[authsList.size()]));

         return  userdetail;
    }

    @Required
     public   void  setUserManager(UserManager userManager) {
         this .userManager  =  userManager;
    }
}

 




最后再来讲说这个命名的问题,我对Authentication和Authority这两个单词比较反感,两个缘由,一是由于它们太生僻了,二是由于它们长得太像了,明明一个是认证,一个是受权,意思相差很远,外貌却如此类似,确实很烦人。若是让我来选择,我喜欢Privilege这个单词,在我刚使用MySQL的时候就跟它很熟了,因此在个人项目中,我可能会用Privilege来代替Authority。若是咱们只使用User-Role两级关系,使用RoleVoter默认的ROLE_前缀固然没有关系,若是是像白衣这样是用三层关系,最好仍是把这个前缀改一改,以避免混淆。安全

相关文章
相关标签/搜索