SpringSide3中的安全框架

在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实现的。如下图:

一般来说,我们的Filter都是配置在web.xml中,但是Acegi不一样,它在web.xml中配置的只是一个代 理,而真正起作用的Filter是作为Bean配置在Spring中的。web.xml中的代理依次调用这些Bean,就实现 了对Web资源的保护,同时这些Filter作为Bean被Spring管理,所以实现AOP也很简单,真的是一举两得啊 。

Acegi中提供的Filter不少,有十多个,一个一个学起来比较复杂。但是对于我们Web开发者来说,常 用的就那么几个,如下图中的被红圈圈标记出来的:

从上到下,它们实现的功能依次是1、制定必须为https连接;2、从Session中提取用户的认证信息;3 、退出登录;4、登录;5、记住用户;6、所有的应用必须配置这个Filter。

一般来说,我们写Web应用只需要熟悉这几个Filter就可以了,如果不需要https连接,连第一个也不 用熟悉。但是有人肯定会想,这些Filter怎么和我的数据库联系起来呢?不用着急,这些Filter并不直接 处理用户的认证,也不直接处理用户的授权,而是把它们交给了认证管理器和决策管理器。如下图:

对于这两种管理器,那也是不需要我们写代码的,Acegi也提供了现成的类。那么大家又奇怪了:又是 现成的,那怎么和我的数据库关联起来呢?别着急,其实这两个管理器自己也不做事,认证管理器把任务 交给了Provider,而决策管理器则把任务交给了Voter,如下图:

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

而DaoAuthenticationProvider也是不直接操作数据库的,它把任务委托给了UserDetailService,如 下图:

而我们要做的,就是实现这个UserDetailService。图画得不好,大家不要见笑,但是说了这么多总算 是引出了我们开发中的关键,那就是我们要实现自己的UserDetailService,它就是连接我们的数据库和 Acegi的桥梁。UserDetailService的要求也很简单,只需要一个返回 org.springframework.security.userdetails.User对象的loadUserByUsername(String userName)方法。 因此,怎么设计数据库都可以,不管我们是用一个表还是两个表还是三个表,也不管我们是用户-授权, 还是用户-角色-授权,还是用户-用户组-角色-授权,这些具体的东西Acegi统统不关心,它只关心返回的 那个User对象,至于怎么从数据库中读取数据,那就是我们自己的事了。

反过来再看看上面的过程,我们发现,即使我们要做的只是实现自己的UserDetailService类,但是我 们不得不在Spring中配置那一大堆的Bean,包括几个Filter,几个Manager,几个Provider和Voter,而这 些配置往往都是重复的无谓的。好在Acegi 2.0也认识到了这个问题,所以,它设计了一个 标签,让Acegi的配置得到了简化。下面是SpringSide 3中的配置的截图,大家可以看看:

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

下面的代码是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 UserManageruserManager;   public UserDetailsloadUserByUsername(StringuserName) throws UsernameNotFoundException,DataAccessException{    Useruser = userManager.getUserByLoginName(userName);     if (user ==  null )       throw  new UsernameNotFoundException(userName +  " 不存在 " );    List < GrantedAuthority > authsList =  new ArrayList < GrantedAuthority > ();     for (Rolerole:user.getRoles()){       for (Authorityauthority:role.getAuths()){        authsList.add( new GrantedAuthorityImpl(authority.getName()));      }    }     // 目前在MultiDatabaseExample的User类中没有enabled,accountNonExpired,credentialsNonExpired,accountNonLocked等属性     // 暂时全部设为true,在需要时才添加这些属性.    org.springframework.security.userdetails.Useruserdetail =  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(UserManageruserManager){     this .userManager = userManager;  }}

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

效果只能是既费时又没有胜利,再聪慧的人也没法成学。

SpringSide3中的安全框架

相关文章:

你感兴趣的文章:

标签云: