Seam 2.1 安全模块框架 文档翻译 005

15.5. 错误消息

安全API内嵌了针对各种安全相关操作的faces messages。下面这个表里面列出了在message.properties文件里面修改这些消息的message keys。如果想要禁用这些消息,只要在资源文件里面将这些错误消息的值置空就ok了。

Table 15.6. 安全消息

Message Key描述
org.jboss.seam.loginSuccessful不用多说了,看字面就知道什么意思了
org.jboss.seam.loginFailed 
org.jboss.seam.NotLoggedIn 
org.jboss.seam.AlreadyLoggedIn 

 

15.6. 验证(Authorization)

Seam安全API里面提供了很多验证权限的途径,你可以在调用组件的时候、执行组件方法的时候以及在页面内部进行调用权限验证组件。这一节介绍了这些验证途径。如果你需要使用任何高级特性(例如基于规则的权限验证),你一定要记住在components.xml文件里面指明(配置方法见前面的章节)。

15.6.1. 核心概念

Seam安全模块就是用来让那些被授予了角色和(或)权限的人能够进行某些操作,并且阻止那些没有被授权的人进行这些相应操作的。Seam安全接口提供了验证用户权限的各种方法,这些方法都基于角色/权限概念。当然Seam安全框架还支持多种安全设置。

15.6.1.1. 什么是角色?

一个角色就是一个组或者说是一种类型。这个组包含了用户可能会被赋予的一组权限(一组被许可在程序中进行的操作集合)。角色的结构很简单,仅仅由一个名称组成,例如“admin”、“user”、“customer”等。角色可以被赋予某个用户(有时也可能将角色赋予某个角色),并且可以用来对拥有相似操作权限的用户分组。

15.6.1.2. 什么是权限?

权限就是进行某个操作(有时是一次性操作)的权利。在权限之上你完全可以不加其他约束(这里指角色),但是角色能够对权限和用户进行分组,这样就能进行更多高级的操作。权限比角色的结构稍微复杂点,主要包含三个属性:一个目标、一个操作、一个容器(recipient)。权限的目标指容器(或者说用户)被授权进行某一特殊操作的对象(可以是一个名称或者一个class)。例如,一个名叫Bob的用户拥有一项权限,就是,他可以删除客户。在这种情况下,删除用户这个权限的对象为“客户”,权限的操作为“删除”,权限的容器为“Bob”。

在这个文档里面,我们通常将权限单纯地理解为上面表中的target:action(我们在这里忽略了容器,但是在实际应用当中,容器通常是需要的)

15.6.2. 安全相关组件

让我们来检验一下验证表单中最简单的情况——组件安全。下面我们从@Restrict注解开始。

@Restrict vs Typesafe security annotations

@Restrict能够使用EL表达式,所以它能够提供功能完善且灵活的验证方法。我们建议使用到的EL表达式应该是类型安全的,起码在编译的时候要保证它的安全。

15.6.2.1. @Restrict注解

Seam组件可以通过使用@Restrict来实现方法级别或者类级别的安全控制。如果一个方法和这个方法所在类同时使用了@Restrict注解,那么方法级别的安全限制优先级更高(意味着类级别的限制不起作用)。如果在方法上验证失败,那么与执行Identity.checkRestriction()方法一样抛出一个异常(参考相关的文档)。把@Restrict加载一个组件类上,相当于将这个约束加在了这个类的所有方法上。

一个空的@Restrict约束默认使用componentName:methodName作为约束。例如以下这段代码:

  
  
@Name( " account " ) public class AccountAction { @Restrict public void delete() { ... } }

在这个例子中,默认调用delete()方法需要account:delete权限。效果与@Restrict("#{s:hasPermission('account','delete')}")相等。

再来看下面这段代码:

  
  
@Restrict @Name( " account " ) public class AccountAction { public void insert() { ... } @Restrict( " #{s:hasRole('admin')} " ) public void delete() { ... } }

这种情况下,类上已经加了@Restrict,这就意味着所有没有加@Restrict的方法都需要进行默认的权限验证。在上面这个例子中,insert()方法需要权限account:insert,而delete()方法需要用户拥有admin角色。

当我们继续进行下一步之前,让我们找到这个表达式:#{s:hasRole()}s:hasRoles:hasPermission都是EL表达式,分别对应Identity类里面的方法。这个方法能够用在所有EL表达式中,并且会用在所有安全验证相关的部分。

作为一个EL表达式,@Restrict注解的值可以关联到任何一个Seam上下文中存在的对象上。这在对某个对象实例进行权限验证的时候非常有用。

  
  
@Name( " account " ) public class AccountAction { @In Account selectedAccount; @Restrict( " #{s:hasPermission(selectedAccount,'modify')} " ) public void modify() { selectedAccount.modify(); } }

上面这个例子中,有一段代码比较有趣。就是在hasPermission()方法内部使用到了selectedAccount对象。此时,Seam会到上下文中寻找这个对象实例,并且将它传递给Identity对象的hasPermission()方法。这样就能判断出当前用户是否有权限来修改Account对象。

15.6.2.2. 内部约束(Inline restrictions)

有的时候你可能会需要在某个方法的代码内部检查权限,这样就无法使用@Restrict注解。这种情况下,你就需要用到Identity.checkRestriction()方法来执行一个与安全有关的表达式,例如:

  
  
public void deleteCustomer() { Identity.instance().checkRestriction( " #{s:hasPermission(selectedCustomer,'delete')} " ); }

如果表达式未返回true则:用户没有登录——抛出NotLoggedInException异常,用户已经登录——抛出AuthorizationException异常。

同样的,你也可以在任何Java代码中使用hasRole()和hasPersmission()方法。

  
  
if ( ! Identity.instance().hasRole( " admin " )) 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 " );
15.6.3. 程序界面中的安全控制(Security in the user interface)

一个良好的用户界面设计需要符合一个特征,用户应该只看见他所能操作的部分,而用户没有权限看见或者没有权限进行操作的按钮链接等,不应该显示出来。Seam提供了两种根据权限控制界面显示内容的方法:1)根据权限判断页面是否显示,2)根据权限判断界面中的某个控件是否显示。这两种控制都使用EL表达式来判断。

我们来看几个权限控制的例子。

首先,一个登录表单需要只有当用户没有登录的时候才能看见。如果用户已经登录,那么就不需要显示登录表单。这个功能可以使用identity.isLoggedIn()属性来判断,在页面上,我们可以写成这样:

  
  
< h:form class ="loginForm" rendered ="#{not identity.loggedIn}" >

这种控制方式非常直观,看见代码的字面意思就应该能明白。下面,我们假设页面上有个显示所有报表的链接,这个链接只有当用户拥有manager角色的时候才能看见。我们在页面上可以这么写:

  
  
< h:outputLink action ="#{reports.listManagerReports}" rendered ="#{s:hasRole('manager')}" > Manager Reports h:outputLink>

这段代码也很直观。如果用户没有manager角色,那么这个链接就不会显示。通常rendered属性可以直接卸载具体的控件内部,但是根据实际情况,也可以将它写在某个父标签上,例如

现在,我们来看一个复杂点的例子。假设页面上有一个h:dataTable,用于显示一个记录列表。表格的最后一列用来显示对当前记录的操作链接,这个链接需要根据用户拥有的权限来判断是否显示。s:hasPermission表达式允许我们将当前行的记录作为参数传入权限判断函数,并根据传入的对象以及当前登录的用户来判断是否显示。下面就是这个表格的代码:

  
  
< h:dataTable value ="#{clients}" var ="cl" > < 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>
15.6.4. 页面安全控制(Securing pages)

页面安全控制需要程序里面有pages.xml文件,不过配置代码非常简单。只需要在page标签内部添加一个 对象就能够实现安全控制。如果restrict对象没有任何属性或者子对象,那么默认情况下,来自non-faces (GET)的请求会需要权限/viewId.xhtml:render, JSF postback(来自表单提交方式)的请求会需要权限/viewId.xhtml:restore。此外,如果想要指定约束规则,那么只要写出标准的安全验证表达式即可。下面是几个例子:

  
  
< page view-id ="/settings.xhtml" > < restrict /> page> <page view-id="/reports.xhtml"> <restrict>#{s:hasRole('admin')} restrict> page>
15.6.5. 实体安全控制(Securing Entities)

Seam安全模块同样允许将实体的添加、修改、删除操作用安全规则来控制。如果想要将对实体的所有操作用Seam安全模块来控制,那么只要在实体类上添加一个@Restrict注解:

  
  
@Entity @Name( " customer " ) @Restrict public class Customer { ... }

如果@Restrict注解内部没有写任何权限判断表达式,那么默认的权限为entityName:action,这里的entityName为Seam组件名称(如果没有使用@Name注解来指定实体的Seam组件名称,那么也可以使用fully-qualified类名称来代替)。冒号后的操作有四种,分别是:read、insert、update、delete。

也可以单独指定某个生命周期操作,在@Restrict注解之外添加相应的注解到对应生命周期的方法上即可。共有以下四种注解:

  • @PostLoad - 当一个实体的实例从数据库中读取出来以后调用。使用这个方法来配置read权限。

  • @PrePersist - 插入数据库之前,insert

  • @PreUpdate - 更新之前,update

  • @PreRemove - 删除之前,delete

下面这个例子介绍了如果给所有insert操作添加安全检查。请注意,这个方法并不需要做任何事情,唯一需要注意的就是,这个方法是如何被声明的:

  
  
@PrePersist @Restrict public void prePersist() {}

你也可以在/META-INF/orm.xml文件中定义:

  
  
xml version="1.0" encoding="UTF-8"?> <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" version="1.0"> <entity class="Customer"> <pre-persist method-name="prePersist" /> entity> entity-mappings>

当然,你仍然需要使用@Restrict来注解Customer对象中的prePersist()方法。

下面这个例子介绍了使用规则判断当前用户是否有权限添加一篇新的MemberBlog记录(来自seamspace示例)。The entity for which the security check is being made is automatically inserted into the working memory (in this case MemberBlog):

  
  
rule InsertMemberBlog no - loop activation - group " permissions " when principal: Principal() memberBlog: MemberBlog(member : member -> (member.getUsername().equals(principal.getName()))) check: PermissionCheck(target == memberBlog, action == " insert " , granted == false ) then check.grant(); end;

This rule will grant the permission memberBlog:insert if the currently authenticated user (indicated by the Principal fact) has the same name as the member for which the blog entry is being created. The "principal: Principal()" structure that can be seen in the example code is a variable binding - it binds the instance of the Principal object from the working memory (placed there during authentication) and assigns it to a variable called principal. Variable bindings allow the value to be referred to in other places, such as the following line which compares the member's username to the Principal name. For more details, please refer to the JBoss Rules documentation.

Finally, we need to install a listener class that integrates Seam security with your JPA provider.

15.6.5.1. 使用JPA实现实体安全控制(Entity security with JPA)

EJB3实体的安全控制通过一个EntityListener来实现。你可以在META-INF/orm.xml写以下代码来来安装这个监听器:

  
  
xml version="1.0" encoding="UTF-8"?> <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" version="1.0"> <persistence-unit-metadata> <persistence-unit-defaults> <entity-listeners> <entity-listener class="org.jboss.seam.security.EntitySecurityListener"/> entity-listeners> persistence-unit-defaults> persistence-unit-metadata> entity-mappings>
15.6.5.2. 使用一个受管的Hibernate Session来实现实体安全控制(Entity security with a Managed Hibernate Session)

如果你在Seam中使用Hibernate的SessionFactory,并且也使用了注解或者orm.xml文件进行映射配置,那么你不需要做任何事情就可以使用实体安全控制。

15.6.6. 类型安全的权限注解(Typesafe Permission Annotations)

Seam provides a number of annotations that may be used as an alternative to @Restrict, which have the added advantage of providing compile-time safety as they don't support arbitrary EL expressions in the same way that @Restrict does.

Out of the box, Seam comes with annotations for standard CRUD-based permissions, however it is a simple matter to add your own. The following annotations are provided in the org.jboss.seam.annotations.security package:

  • @Insert

  • @Read

  • @Update

  • @Delete

想要使用这些注解,只需要将它写在你想要检查权限的方法或者参数前面。如果放在方法前面,那么你需要指定一下需要检查权限的目标的class:

  
  
@Insert(Customer. class ) public void createCustomer() { ... }

在这个例子里面,当用户尝试创建一个新的Customer对象的时候,会去检查用户是否有相应权限。需要检查权限的对象为Customer.class(实际上是其本身的java.lang.Class实例),而需要判断权限的操作为insert。

这种注解同样也可以用在方法的参数上面。如果加在参数上,那么你不需要指定权限验证的目标(因为参数值本身就将作为权限验证的目标):

  
  
public void updateCustomer(@Update Customer customer) { ... }

如果你想要编写自己的权限验证注解,只需要额外添加@PermissionCheck即可,例如:

  
  
@Target({METHOD, PARAMETER}) @Documented @Retention(RUNTIME) @Inherited @PermissionCheck public @ interface Promote { Class value() default void . class ; }

如果你想覆盖默认的权限判断操作,那么你可以在@PermissionCheck中指定你想覆盖的操作名称(小写):

  
  
@PermissionCheck( " upgrade " )
15.6.7. 类型安全的角色控制注解(Typesafe Role Annotations)

除了权限注解之外,Seam安全模块同样也提供了类型安全的角色注解。这些注解允许你根据用户所拥有的角色来判断是否能够进行相关操作。Seam单独提供了这么一个注解:org.jboss.seam.annotations.security.Admin。这个注解用来判断用户是否拥有admin角色(只要你的程序里面支持这个角色)。想要创建你自己的角色判断注解,只需要额外添加一个org.jboss.seam.annotations.security.RoleCheck注解,例如:

  
  
@Target({METHOD}) @Documented @Retention(RUNTIME) @Inherited @RoleCheck public @ interface User {}

编写了上面这个注解以后,只要添加了这个注解的地方,就会自动去检查用户是否拥有user角色(注解名称的小写形式)。

15.6.8. 权限验证的模型(The Permission Authorization Model)

Seam安全模块提供了一个扩展应用程序权限验证功能的框架。下面的类图展现了权限验证框架的主要组件:

上图中涉及到的类,将在后面的章节中详细介绍。

15.6.8.1. PermissionResolver组件

这个组件实际上是一个接口。提供了解析对象权限的方法。Seam提供了下面两个内置的PermissionResolver实现。后面的章节中会详细介绍它们。

  • RuleBasedPermissionResolver - 这个权限解析器使用Drools来解析基于规则的权限验证。

  • PersistentPermissionResolver - 这个权限解析器用来解析持久化的对象权限,例如将权限存储在数据库中。

15.6.8.1.1. 编写你自己的PermissionResolver

自定义权限解析器非常简单。PermissionResolver接口定义了两个必须实现的方法,这两个方法列举在下面的表格中。当你将自定义的权限解析器部署到你的项目中去的时候,它就会自动检查并注册到默认的解析器组中去。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值