Seam安全API为安全访问组件、组件方法和页面提供了几种授权机制。本节将描述他们。一个需要注意的重要事项是如果你想使用任何高级特性(比如基于规则的权限),你需要在components.xml
中配置它们以得到支持。(参见配置一节)。
Seam安全的建立是围绕着用户被赋予角色并且/或者权限,允许它们执行那些在没有必要的安全特权就不能执行的操作的前提下的。Seam安全API提供的每一种授权机制都建立在角色和权限这些核心概念之上,这些概念拥有一个提供多种方式保护应用资源的易扩展的框架。
角色是被赋予某些特权,可以执行一个和多个应用中的动作的用户的一个 组 或 类别。简单的角色仅仅由一个名称组成(比如"admin","user","customer"等等)。可以将角色赋予用户(或者其他的角色),也可以被用来为方便分配指定的特权而创建用户的逻辑组。
权限是一种执行一个单独的、特定的动作的特权(有时是一次性的)。一个应用只使用权限是完全可能的,然而角色在给一组用户赋予权限是提供了更高级别的便利 性。权限的结构比角色稍复杂,主要由三个方面构成:目标、动作和接受者。权限的目标是一个对象(或者一个任意名称和类),允许指定的接受者(或用户)执行 这个对象的一个特定的动作。例如,用户“Bob”有权限删除customer对象。在这个例子中,权限目标是“customer”,权限动作是 “delete”,权限的接受者是“Bob”。
本文中,权限通常以target:action
这种形式表现(忽略了接受者,尽管实际上一个接受者是必须的)。
让我们以@Restrict
注释来研究最简单的授权和组件安全为开端。
@Restrict与类型安全的安全注释
由于@Restrict
注释支持EL表达式,使得该注释为保护组件方法提供强大并且灵活的方法,建议使用类型安全的等价物(以后描述),至少在编译期间是安全的。
使用@Restrict
注释,可以在方法和类级别上保护Seam组件。如果一个方法和它声明类都使用了@Restrict
注释,方法的约束将有更高的优先级(并且类约束将不起作用)。如果一个方法调用在安全检查使失败,按照Identity.checkRestriction()
的约定,它将抛出一个异常(参见内联约束)。在组件类级别上的@Restrict
注释等价于它每个方法都使用了这个注释。
@Restrict
意味着一个默认的权限检查:componentName:methodName
。如下所示:public class AccountAction {
@Restrict public void delete() {
}
}
delete()
方法时需要的隐含权限是account:delete
。与@Restrict("#{s:hasPermission('account','delete')}")
这样写是等价的。现在,让我们看看另外一个例子:
public class AccountAction {
public void insert() {
}
@Restrict( " #{s:hasRole('admin')} " )
public void delete() {
}
}
@Restrict
注释。这意味着任何没有重写@Restrict
注释的方法都需要一个隐含的权限检查。在这个例子中,insert()
方法需要一个account:insert
权限,而delete()
方法需要用户是"admin"角色的一个成员。
在继续深入前,先解释一下上面看见的#{s:hasRole()}
表达式。s:hasRole
和s:hasPermission
都是EL函数,贯穿整个安全API,这些函数能在任何EL表达式是使用。
@Restrict
注释的值可以引用存在于Seam上下文中的任何对象。这在为一个指定的对象实例执行权限检查是非常有用的。看这个例子:
public class AccountAction {
@In Account selectedAccount;
@Restrict( " #{s:hasPermission(selectedAccount,'modify')} " )
public void modify() {
selectedAccount.modify();
}
}
hasPermission()
函数调用了selectedAccount
方法的引用。该变量值将在Seam上下文中查找,并传递给Identity
的hasPermission()
方法,在本例中它将确定用户是否有必须的修改指定Account
对象的权限。
有时它也可以在声明在代码中来执行安全检查,而不使用@Restrict
注释。在这种情况下,简单的使用Identity.checkRestriction()
来评估安全表达式,就像这样:
Identity.instance().checkRestriction( " #{s:hasPermission(selectedCustomer,'delete')} " );
}
true
,并且
如果用户没有登录,将抛出
NotLoggedInException
异常,或者如果用户已经登录,将抛出
AuthorizationException
异常。
hasRole()
和hasPermission()
方法:
throw new AuthorizationException( " Must be admin to perform this action " );
if ( ! Identity.instance().hasPermission( " customer " , " create " ))
throw new AuthorizationException( " You may not create new customers " );
作为一个优良设计的用户界面的指标之一就是用户不会看见它没有权限使用的选项。基于以上的用户的特权,使用非常一致的组件安全性EL表达式,Seam安全允许有条件的渲染 1)页面的区段和 2)独立的控件。
让我们看看一些界面安全的例子。首先,假设我们有一个登录表单,它只能在用户没有登录的时候被渲染。使用identity.isLoggedIn()
特性,我们可以这样写:
manager
”角色的用户访问的动作。可以写成这样:
Manager Reports
</ h:outputLink >
manager
角色的成员,那么这个outputlink将不会被渲染。rendered
属性通常是控件本身使用,或者是一个被包围的<s:div>
或<s:span>
控件。
现在看看一些更复杂的。假设你有一个h:dataTable
控件,其中的数据列表中你希望依靠用户的权限来决定是否被渲染而显示。s:hasPermission
这个EL函数允许我们传递一个可以决定是否用户拥有要求的权限的对象参数。这样的被保护的dataTable可能像这样:
< h:column >
< f:facet name ="header" > Name </ f:facet >
#{cl.name}
</ h:column >
< h:column >
< f:facet name ="header" > City </ f:facet >
#{cl.city}
</ h:column >
< h:column >
< f:facet name ="header" > Action </ f:facet >
< s:link value ="Modify Client" action ="#{clientAction.modify}"
rendered ="#{s:hasPermission(cl,'modify')" />
< s:link value ="Delete Client" action ="#{clientAction.delete}"
rendered ="#{s:hasPermission(cl,'delete')" />
</ h:column >
</ h:dataTable >