`
xyz20003
  • 浏览: 289644 次
  • 性别: Icon_minigender_1
  • 来自: 唐山
社区版块
存档分类
最新评论

(翻译)Spring Security-2.0.x参考文档“领域对象安全”

阅读更多
领域对象安全
24.1. 概述

请注意:在2.0.0之前,Spring Security还叫Acegi Security。 在老的Acegi Security发布里提供了一个ACL模块,放在 org.[acegisecurity/springsecurity].acl下。 旧包现在被废弃了,会在Spring Security未来发布里删除。 这章提到的新ACL模块,是由Spring Security 2.0.0正式推荐的,可以在org.springframework.security.acls包找到。

复杂程序常常需要定义访问权限,不是简单的web请求或方法调用级别。而是,安全决议需要包括谁(认证),哪里(MethodInvocation)和什么(一些领域对象)。 换而言之,验证决议也需要考虑真实的领域对象实例,方法调用的主体。

想像我们为宠物店设计一个程序。 在你的基于Spring程序里有两个主要的用户组:宠物商店的工作人员和宠物商店的顾客。 工作人员可以访问所有数据,而你的顾客只能看到他自己的数据。 让它更有趣一点儿,你的客户可以允许其他用户看他自己的数据,比如他们“学龄前小狗”教练,或他们本地“小马俱乐部”的负责人。 以Spring Security为基础,我们可以使用很多方法:

   1.

      编写你的业务方法来提升安全。 你可以使用一个集合,包含 Customer领域对象实例,来决定哪个用户可以访问。 通过SecurityContextHolder.getContext().getAuthentication(), 你可以得到Authentication对象。
   2.

      编写一个 AccessDecisionVoter 提升安全性,通过保存在Authentication对象里的GrantedAuthority[]。 这意味着你的AuthenticationManager需要使用自定义GrantedAuthority[]组装这个Authentication,处理每个主体访问的Customer领域对象实例。
   3.

      编写一个 AccessDecisionVoter 提升安全性,直接打开目标Customer领域对象。 这意味着你的投票者需要访问一个DAO,允许它重审Customer对象。 它会访问用户提交的Customer对象的集合,然后执行合适的决议。

每个方法都是完全可用的。 然而,你的第一种认证会涉及你的业务代码。 它的主要问题是单元测试困难,也很难在其他地方重用Customer的授权逻辑。 从Authentication获得GrantedAuthority[]也还好,但是不适合大规模数量的Customer。 如果用户可以访问五万个Customer(不是在这个例子里,但是想像一下,如果它是一个大型的小马俱乐部),这么大的内存消耗,和时间消耗,建造Authentication是不可取的。 最后一个方法,直接从外部代码打开Customer,可能是三个中最好的了。 它分离了概念,没有滥用内存或CPU周期,但它还是没什么效率,在AccessDecisionVoter和最终业务方法里,它自己会执行一个DAO响应,来重申 Customer对象。 每个方法调用,都要评估两次,非常不可取。 另外,每个方法列出了你需要,从头写自己访问控制列表(ACL)持久化和业务逻辑。

幸运的是,这里有另一个选择,我们在下面讨论。
24.2. 关键概念

Spring Security的ACL服务放在spring-security-acl-xxx.jar中。 你需要把这个JAR添加到你的classpath下,来使用Spring Security的领域对象实例安全能力。

Spring Security的领域对象实例安全能力其实是一个访问控制列表(ACL)的概念。 在你的系统中每个领域对象实例都有它自己的ACL,然后这个ACL数据信息,谁可以,谁不可以和领域对象工作。 在这种思想下,Spring Security在你的系统提供三个主要的ACL相关能力:

    *

      一个有效的方法,为所有你的领域对象(修改那些ACL)检索ACL条目。
    *

      一个方法,在方法调用之前确认给定的主体有权限同你的对象工作。
    *

      一个方法,在方法调用之后确认给定的主体有权限同你的对象工作(或它返回的什么东西)。

像第一点所示,Spring Security的ACL模块的一个主要能力,是提供高性能的检索ACL。 这个ACL资源能力特别重要,因为在你的系统中每个领域对象实例,可能有多个访问控制条目,每个ACL可能继承其他ACL,像一个树形结构(这是Spring Security支持的,非常常用)。 Spring Security的ACL能力仔细定义来支持高性能检索ACL,可插拔缓存,最小死锁数据库更新,不依赖ORM(我们直接使用的JDBC),适当封装,数据库透明更新。

给定的数据库是ACL模块操作的中心,让我们来看看默认实现使用的四个主要表。 下面介绍的这些表,为了Spring Security ACL的部署,使用的表在最后列出:

    *

      ACL_SID让我们定义系统中唯一主体或授权(“SID”意思是“安全标识”)。 它包含的列有ID,一个文本类型的SID,一个标志,用来表示是否使用文本显示引用的主体名或一个GrantedAuthority。 因此,对每个唯一的主体或GrantedAuthority都有单独一行。 在使用获得授权的环境下,一个SID通常叫做"recipient"授予者。
    *

      ACL_CLASS 让我们在系统中确定唯一的领域对象类。包含的列有ID和java类名。 因此,对每个我们希望保存ACL权限的类都有单独一行。
    *

      ACL_OBJECT_IDENTITY 为系统中每个唯一的领域对象实例保存信息。列包括ID,指向ACL_CLASS的外键,唯一标识,所以我们知道为哪个ACL_CLASS实例提供信息,parent,一个外键指向ACL_SID 表,展示领域对象实例的拥有者,我们是否允许ACL条目从任何父亲ACL继承。 我们对每个领域对象实例有一个单独的行,来保存ACL权限。
    *

      最后,ACL_ENTRY保存分配给每个授予者单独的权限。 列包括一个ACL_OBJECT_IDENTITY的外键,recipient(比如一个ACL_SID外键),我们是否通过审核,和一个整数位掩码,表示真实的权限被授权或被拒绝。 我们对于每个授予者都有单独一行,与领域对象工作获得一个权限。

像上一段提到的,ACL系统使用整数位掩码。 不要担心,你不需要知道使用ACL系统位转换的好处,但我们有充足的32位可以转换。 每个位表示一个权限,默认授权是可读(位0),写(位1),创建(位2),删除(位3)和管理(位4)。 如果你希望使用其他权限,很容易实现自己的Permission实例,其他的ACL框架部分不了解你的扩展,依然可以运行。

了解你的系统中领域对象的数量很重要,完全用不害怕我们选择使用整数位掩码的事实。虽然我们有32位可用来作权限,你可能有几亿领域对象实例(意味着在ACL_OBJECT_IDENTITY表中有几亿行,ACL_ENTRY也很可能是这样)。我们说出这点,因为我们有时发现人们犯错误,决定他们为每个潜在的领域对象提供一位,情况并非如此。

现在我们提供了ACL系统可以做的基本概述,它看起来像一个表结构,现在让我们探讨关键接口。 关键接口是:

    *

      Acl: 每个领域对象有一个,并只有一个Acl对象,它的内部保存着AccessControlEntry,记住这是Acl的所有者。 一个Acl不直接引用领域对象,但是作为替代的是使用一个ObjectIdentity。 这个Acl保存在ACL_OBJECT_IDENTITY表里。
    *

      AccessControlEntry: 一个 Acl 里有多个AccessControlEntry,在框架里常常略写成ACE。 每个ACE引用特别的Permission,Sid和Acl。 一个ACE可以授权或不授权,包含审核设置。 ACE保存在ACL_ENTRY表里。
    *

      Permission: 一个 permission 表示特殊不变的位掩码,为位掩码和输出信息提供方便的功能。 上面的基本权限(位0到4)保存在BasePermission类里。
    *

      Sid: 这个 ACL 模块需要引用主体和GrantedAuthority[]。 间接的等级由Sid接口提供,简写成“安全标识”。 通常类包含PrincipalSid(表示主体在Authentication里)和GrantedAuthoritySid。 安全标识信息保存在ACL_SID表里。
    *

      ObjectIdentity: 每个领域对象放在ACl模型的内部,使用ObjectIdentity。 默认实现叫做ObjectIdentityImpl。
    *

      AclService: 重审Acl对应的ObjectIdentity。 包含的实现(JdbcAclService),重审操作代理LookupStrategy。 这个LookupStrategy为检索ACL信息提供高优化策略,使用批量检索(BasicLookupStrategy)然后支持自定义实现,和杠杆物化视图,继承查询和类似的表现中心,非ANSI的SQL能力。
    *

      MutableAclService: 允许修改了的 Acl放到持久化中。 如果你不愿意,可以不使用这个接口。

请注意,我们的AclService和对应的数据库类都使用ANSI SQL。 这应该可以在所有的主流数据库上工作。 在写作的时候,系统成功在Hypersonic SQL, PostgreSQL, Microsoft SQL Server 和 Oracle上测试通过。

Spring Security的两个实例演示了ACL模块。 第一个是Contacts实例,另一个是文档管理系统(DMS)实例。 我们建议大家看一看这些例子。
24.3. 开始

为了开始使用Spring Security的ACL功能,你会需要在一些地方保存你的ACL信息。 有必要使用Spring的DataSource实例。 DataSource注入到JdbcMutableAclService和BasicLookupStrategy实例中。 后一个提供了高性能ACL检索能力,前一个提供变异能力。 参考例子之一,使用Spring Security,为一个例子配置。 你也需要使用四个ACL指定的表建立数据库,这写在最后一章(参考ACL实例,查看对应的SQL语句)。

一旦你创建了需要的结构,和JdbcMutableAclService的实例,你下一个需求是确认你的领域模型支持Spring Security ACL包的互操作。 希望的ObjectIdentityImpl会证明足够,它提供可以使用的大量方法。 大部分人会使用领域对象,包含public Serializable getId()方法。 如果返回类型是long或与long兼容(比如int),你会发现你不需要为ObjectIdentity进行更多考虑。 ACL模块的许多部分对应long标识符。 如果你没有使用long(或int, byte等等),你需要重新实现很多类。 我们不倾向在Spring Security ACL模块中支持非long标识符,因为所有数据库序列都支持,最常用的数据类型标识,也可以容纳所有常用场景的足够长度。

下面的代码片段,显示如何创建一个Acl,或修改一个存在的 Acl:

// Prepare the information we'd like in our access control entry (ACE)
ObjectIdentity oi = new ObjectIdentityImpl(Foo.class, new Long(44));
Sid sid = new PrincipalSid("Samantha");
Permission p = BasePermission.ADMINISTRATION;

// Create or update the relevant ACL
MutableAcl acl = null;
try {
  acl = (MutableAcl) aclService.readAclById(oi);
} catch (NotFoundException nfe) {
  acl = aclService.createAcl(oi);
}

// Now grant some permissions via an access control entry (ACE)
acl.insertAce(acl.getEntries().length, p, sid, true);
aclService.updateAcl(acl);

在上面的例子里,我们检索ACl,分配给"Foo"领域对象,使用数字44作标识。 我们添加一个ACE,这样名叫"Samantha"的主体可以“管理”这个对象。 代码片段是自解释的,除了insertAce方法。 insertAce方法的第一个参数是Acl里新条目被插入的决定位置。 在上面的的例子里,我们只把新ACE放到以存在的ACE的尾部。 最后一个参数是一个布尔值,显示是否ACE授权或拒绝。 大多数时间,是授权(true),如果它是拒绝(false),权限就会被冻结。

Spring Security 没有提供任何特定整合,自动创建,更新,或删除ACL,作为你的DAO的一部分或资源操作。 作为替代的,你会需要像上面一样为你的单独领域对象写代码。 值得考虑在你的服务层使用AOP,来自动继承ACL信息,使用你的服务层操作。 我们发现以前这是一个非常有效的方式。

一旦,你使用上面的技术,在数据库里保存一些ACL信息,下一步是使用ACL信息,作为授权决议逻辑的一部分。 这里你有一大堆选择。 你可以写你自己的AccessDecisionVoter或AfterInvocationProvider,期待在方法调用之前或之后触发。 这些类使用AclService来检索对应的ACL,然后调用Acl.isGranted(Permission[] permission, Sid[] sids, boolean administrativeMode),决定权限是授予还是拒绝。 可选的,你可能使用我们的AclEntryVoter,AclEntryAfterInvocationProvider或AclEntryAfterInvocationCollectionFilteringProvider类。 所有这些类提供一个基于声明的方法,在运行阶段来执行ACL信息,释放你从需要写任何代码。 请参考例子程序,学习更多如何使用这些类。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics