`
stenlylee
  • 浏览: 255438 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Seam中基于ACL的权限控制(1)

阅读更多

Seam框架已经能够解决大多数web程序开发中会遇到的问题。通过提供一系列基于“最佳实践”总结出的统一模型,开发人员的web程序的开发工作变得非常轻松。程序员在开发具体的相关业务逻辑的时候,就不会再郁闷了,因为大多数功能在Seam中都有对应的模块来实现。例如,Seam生成PDF、发送email、实现国际化等,都非常方便。同时,Seam还集成了许多第三方框架,例如Drools、jBPM等,这些框架写作能够实现非常复杂的业务逻辑以及跨度时间很长的流程操作。其他还支持自动生成验证码、wiki风格的标记语言和一大堆AJAX功能。

 

其中最重要的企业特性之一,就是Seam的权限控制部分。Seam包含一个非常健全的安全认证API,通过这个API,一个典型的程序中需要用到的组件和视图安全认证都能够实现。Seam通过用户角色以及基于规则的权限控制来实现安全控制。最近的版本中(2.1.0GA),Seam重写了安全引擎,提供了大量新的相关功能来保障敏感数据的安全。本文就来看看其中一个新的特性——权限的持久化,着重于介绍ACL或者说是基于实例的安全机制是如何在对象级别保障你程序的安全的。

 

开始之前,让我们先来看看基于规则的与基于ACL的安全控制有什么不同。基于规则的安全控制擅长于将一堆权限授予某个对象。例如下面这段规则代码(来自于Seam的示例项目):

 

  rule DeleteImage
    no-loop
    activation-group "permissions"
  when
    acct: MemberAccount()
    image: MemberImage(mbr : member -> (mbr.memberId.equals(acct.member.memberId)))
    check: PermissionCheck(target == image, action == "delete", granted == false)
  then
    check.grant();
  end

 

这段规则做了这么一个限制:让用户有权限删除他之前上传的图片。其中最重要的部分限制了,只有图片的所有者才能删除图片。在这个例子中,该权限加在所有图片上,并且在图片和其所有者之间拥有一种连接关系。通过这条关系,安全规则能够决定当前用户即为该图片的所有者,并且将相应的权限授予其执行操作。不过,如果在需要检查的对象的权限如果与当前用户没有关联呢?(在这里,用户就是只principal)这样就需要ACL了。

 

ACL(访问控制表)记录了某个指定的对象与某个权限之间的一一对应关系。每个记录都拥有一个接受者(被授予权限的用户principal)和一个操作。如果你使用过unix系列的操作系统,那么你就会对ACL非常熟悉了,unix操作系统有一个表格用来记录某个用户对某个文件的修改、删除或执行权限。当然Windows也有这样的机制,不过Windows并没有表现出他是这样控制的。Seam中的ACL也是一样的,唯一不同的就是,Seam中ACL记录的对象是实体的实例,而不是文件。通常情况下,这个对象为实体,不过我们将会看见,它可以应用到所有类型的对象上。

 

当我们为对象指定权限的时候,我们需要先来做点准备工作。其中最重要的,就是为我们的权限准备一个存储方式。Seam提供了一个PermissionStore接口,这个接口中定义了各种基于ACL的对象权限管理方法。理论上,权限的存储方式可以为所有可能的方式,例如文件、LDAP等等。不过通常,我们都用关系数据库来存储。所以Seam提供了一个PermissionStore的实现——JpaPermissionStore,它使用JPA来管理存储在数据库中的权限。要想使用JpaPermissionStore,我们需要做两件事情,第一是写一个Entity,来映射管理数据库中的权限记录,第二是在component.xml中添加相应的配置文件。

 

 

Seam提供一组专用于权限的注解,用来将Entity中的属性与数据库中的字段对应起来。下面这段代码为配置权限Entity的最简情况(为了版面简介,这里省略了所有的getter与setter):

 

@Entity
public class AccountPermission implements Serializable
{          
   @Id @GeneratedValue public Integer permissionId;
   @PermissionUser @PermissionRole public String recipient;
   @PermissionTarget public String target;
   @PermissionAction public String action;
   @PermissionDiscriminator public String discriminator;
}

 

@PermissionUser与@PermissionRole注解用来指定权限的接受者。在这里,我们用一张表来管理角色的权限与人员的权限,所以我们将这两个注解加到同一个属性上。同时,为了做到这一点,我们需要一个专门的字段来当作辩析器,指明这条权限是针对角色的还是针对用户的,我们将@PermissionDiscriminator加到另外一个属性上,就能将一个字段变为辩析器。最后,我们需要一个字段来指明权限适用的对象(@PermissionTarget)与权限的操作(@PermissionAction)。

 

下面我们需要在components.xml文件中添加相应的配置了,添加这个配置信息,目的是为了启用JpaPermissionStore:

 

<security:jpa-permission-store user-permission-class="com.acme.AccountPermission" />

 

现在我们配置也做了,Entity也写了,下面我们就可以开始分配权限了。Seam提供了一个组件,叫做PermissionManager,帮助我们完成权限管理方面的工作。PermissionManager里面的方法与PermissionStore接口里面的非常类似,实际上它也是将大多数工作递交给底层的PermissionStore实现,唯一不同的地方就是,它在执行相关操作之前,会先检查一下当前用户是否拥有该操作所对应的权限。更多相关的内容,请参考Seam的文档。然而,不是所有用户都有权管理权限的,必须要有相关的授权才可以。

 

让我们来授予用户第一个权限!假设程序里面有一个customer表,对应的Entity为Customer,你需要将不同客户的资料管理权限授予不同的销售人员。加入你需要张三去管理你最好的客户——李四(对应的客户编号为1234)。我们将客户李四资料的维护权限授予张三的代码会是下面这样:

 

PermissionManager.instance().grantPermission(new Permission(entityManager.find(Customer.class, 1234), "update", new SimplePrincipal("bob")));

 

PermissionManager的实例会调用grantPermission()方法来将新的权限生成一条记录,然后保存进数据库。这个操作会自动递交给你配置的JpaPermissionStore来执行(当然,这个操作首先会检查当前用户是否有权进行)。我们来看一下这个操作在AccountPermission产生了什么效果:

AccountPermission
=================
RECIPIENT  TARGET         ACTION  DISCRIMINATOR
-----------------------------------------------
bob        Customer:1234  update  user

 

我们可以从数据库查询结果中很显然地看到,这条权限的接受者是bob,对象是编号为1234的Customer,操作是更新,并且这条对象是授权给bob这个用户而不是某个名叫bob的角色,因为辨别器中写的是user而不是role。

 

其中target的值比较有意思,我们可以看见,这个字段的值有两个部分,冒号前面的部分是对象的名称Customer,而冒号后面为对象的ID。在Entity中,Seam可以自动生成对象的ID。

 

但是,如果我们需要记录的权限对象不是一个实体,而是其他的类,那么就需要让Seam知道如何来生成针对某个任意对象的唯一标识符。我们可以添加@Identifier到相应的类上,并且添加一个实现了IdentifierStrategy接口的类。例如:

 

@Identifier(CustomIdentifierStrategy.class)
public class MyNonEntityClassThatIWantToAssignPermissionsTo {
   public String getUniqueProperty() { return foo; }
}

 

IdentifierStrategy也非常简单,只要实现两个方法就可以了。canIdentify()在IdentifierStrategy能够为指定的类生成唯一标识符的时候返回true,getIdentifier() 则为指定的对象返回生成的唯一标识符。例如,Seam内置实现的EntityIdentifierStrategy类就是为某个Entity生成一个调用对象编号的name。

 

 

public class CustomIdentifierStrategy implements IdentifierStrategy {
  public boolean canIdentify(Class targetClass) {
    return targetClass.equals(MyNonEntityClassThatIWantToAssignPermissionsTo.class);
  }
  public String getIdentifier(Object target) {
    return ((MyNonEntityClassThatIWantToAssignPermissionsTo) object).getUniqueProperty();
  }  
}

 

还有值得一提的,你也可以为某个字符串常量指定权限。例如,权限的对象不一定非要是Entity或者某个类。PermissionManager同样也很乐意接受一个简单的字符串作为权限对象,因为有可能你需要的一个权限,仅仅是你根据需要任意指定的一个权限而已。对于一个普通的类,你就可以这么做,例如你想针对一个类创建一条权限,名叫“创建”,然后用这个权限来限制用户能否创建一个类的实例(new ClassName的方式)。不过你无法创建一个基于类的权限去作用于一个不存在的类。

 

通过上面介绍的这些,你已经可以通过Seam内置的权限管理模块来对你程序里涉及到的权限进行控制了。具体的在Seam官方文档里面有介绍。

 

下一篇文章我们将会一起来探讨,权限相关的操作是如何定义并且存储的。并且我也会介绍一下如何创建用户体验良好的权限管理界面,来简化权限管理方面的操作。

 

敬请期待

  • 大小: 4.4 KB
  • 大小: 1.7 KB
分享到:
评论
3 楼 Visa 2009-05-11  
2 楼 ltj_007 2009-03-05  
   
1 楼 wan_2004 2009-02-27  
 

相关推荐

Global site tag (gtag.js) - Google Analytics