Seam安全升级

Seam安全升级

by Shane Bryzak

 Article permalink: http://shane.bryzak.com/articles/seam_security_gets_an_upgrade

 

即将到来的 JBoss Seam 2.1.0.GA 发布版本将包含一些新的功能和增强的安全特性,这些安全特性包括身份管理, 基于ACL的权限和权限管理, 增强型(strongly-typed)安全注释。在这篇文章中,我将结合在SeamSpace例子(在Seam发布包的/examples/seamspace目录下)来讲解下这些新的安全特性。

 

如果你想自己来运行SeamSpace例子,你需下载它运行所需的软件并配置它们,步骤如下:

 

1) 首先,确保你安装了JDK 1.5和最近发布版的 Apache Ant。

2) 下载并安装 JBoss AS 4.2.2, 下载地址: http://www.jboss.org/jbossas/downloads/

3) 下载并且解压缩最新的Seam 2.1每日构建版,下载地址: http://www.seamframework.cn/Download (如果你具觉得有风险,可从我们匿名SVN库中查看最新的版本,地址:http://anonsvn.jboss.org/repos/seam/trunk/)

4) 编辑Seam发布包目录中的 build.properties 文件,修改你的 JBoss AS 安装目录

5) 进入Seam发布包的examples/seamspace 目录,运行 'ant'

6) 进入JBoss AS的bin目录,运行run脚本启动JBoss AS

7) 在JBoss AS 启动后,浏览http://localhost:8080/seam-space

 

让我们先看看新的身份管理特性。

 

身份管理

 

那么, 身份管理体制是什么呢? 到目前为止,Seam只提供了内建的组件用于协助用户认证(Identity 组件)。Seam没有提供一个正式的API,用于创建和管理你认证的真实的用户帐号,这差不多留给了开发者。身份管理提供这样一个API弥补了这个空白,其尽力提供一致的方式管理用户和角色,不管它们是如何储存在后端。无论它们作为记录被持久化在关系数据库中,还是作为实体存储在LDAP目录中,身份管理提供了一个标准的API,用于在一个Seam应用程序中来创建,更新,删除用户和角色。

 

身份管理 API 的核心是 IdentityManager 组件。这个组件暴露了大量身份管理的操作。为了让你了解它提供的更多特性,这里是列出了它的一些的方法:

 

    * createUser(String username, String password)

    * deleteUser(String name)

    * enableUser(String name)

    * disableUser(String name)

    * changePassword(String name, String password)

    * isUserEnabled()

    * grantRole(String name, String role)

    * revokeRole(String name, String role)

    * createRole(String role)

    * deleteRole(String role)

    * userExists(String name)

    * roleExists(String name)

    * listUsers()

    * authenticate(String username, String password)

 

从上表中你可以看到,大多数的方法都和操作系统处理安全的常见命令同义(如果你特别地熟悉unix/linux),如adduser,deluser 等等。

 

配置 IdentityManager

 

配置IdentityManager是相当简单的 —— 它必须和一个或两个身份库(identity stores)一起配置。一个身份库用于所有用户相关的操作,另外一个用于角色相关的操作。如果你的用户和角色都包含在同一个持久化库中(例如一个数据库),那么只需要配置一个身份库,它会被用于用户和角色的操作。这听起来有点奇怪,可是,在处理复杂的安全需求时,其允许更大的灵活性。例如,它使用LDAP认证用户的情况成为可能,只从一个关系数据库加载它们的角色。

 

每个身份库(IdentityStore)实现知道如何处理一个特定类型的持久化安全库。Seam提供了两个开箱即用的身份库(IdentityStore)实现,JpaIdentityStore 和LdapIdentityStore,用于数据库或基于LDAP的安全认证(分别地)。如果没有配置IdentityManager的身份库(identity stores),那么它默认使用JpaIdentityStore。以后我们会研究LdapIdentityStore,以及如何设置Seam根据一个LDAP目录进行认证——现在我们关注JpaIdentityStore。

 

JpaIdentityStore

 

这个身份库(IdentityStore)实现允许你使用一个关系数据库存储用户帐户的详细信息。通过用一组特定的注释来注释表示用户和角色的实体bean,JpaIdentityStore能使用这些实体来管理作为记录被持久在数据库中的用户账号。

 

让我们看看被用来存储用户帐号的记录的MemberAccount实体bean,。下面的类被删节和重排,只描述它的相应片段:

 

@Entity

@Table(uniqueConstraints = @UniqueConstraint(columnNames = "username"))

public class MemberAccount implements Serializable

{

     // snip field and key declarations

   

     @NotNull @UserPrincipal

     public String getUsername() { return username; }

     public void setUsername(String username) { this.username = username; }

   

     @UserPassword(hash = "MD5")

     public String getPasswordHash() { return passwordHash; }

     public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; }    

   

     @UserEnabled

     public boolean isEnabled() { return enabled; }

     public void setEnabled(boolean enabled) { this.enabled = enabled; }

 

     @UserRoles

     @ManyToMany(targetEntity = MemberRole.class)

     @JoinTable(name = "AccountMembership",

           joinColumns = @JoinColumn(name = "AccountId"),

           inverseJoinColumns = @JoinColumn(name = "MemberOf"))

     public Set<MemberRole> getRoles() { return roles; }

     public void setRoles(Set<MemberRole> roles) { this.roles = roles; }

}

 

就如我们看到的,在bean的属性访问方法中有一些附加的注解,这些注解告诉 JpaIdentityStore 如何与特定的实体bean交互。我们下面来详细的看看这些注解:

 

    * @UserPrincipal - 该注解表明该属性包含用户的主要属性(i.e. 用户名称)

    * @UserPassword - 该注解表明该属性包含用户的密码。把用户密码保存为普通文本通常不是一个好的注意,因此这个注解支持使用hash算法。该算法用来产生一个用户密码

 

hash值,然后会把hash值保存到数据库中。

    * @UserEnabled - 这个是可选的,用户指示该用户帐户是否是启用的,默认值为启用所有的帐户。

    * @UserRoles - 这个注解指示该用户所属的角色组。

 

这里也有一组相似的注解用来配置一个存储角色的实体bean。另外,在同一个表中如存储用户一样存储角色记录是可以的(然后通过自身的一对多关联变成自我引用),通过指定一个列作为一个鉴别器(决定是否这个记录代表一个用户或一个角色)。所描述的这些,以及其他各种选项,更多的细节请看Seam参考文档。

 

使用 IdentityManager

 

现在我们来看看SeamSpace例子,了解如何使用身份管理(Identity Management)。

Seam安全升级 - *工* - 要有光,于是就有了光

 

从上面的SeamSpace主页屏幕截图,我们可以看到允许一个新用户注册的'SIGN UP'链接。在注册页面用户可以输入一些基本信息,创建一个新帐号,以便他们登陆。注册的程序使用IdentityManager来创建新的用户帐户,然后登陆新注册的用户。看一下 RegisterAction.java 类,我们可以看到身份管理(Identity Management)通过使用 @In 注解被注入:

 

   @In

   private IdentityManager identityManager;

 

 

接下来,我们可以在uploadPicture()方法中看到这个新用户如何被创建(在注册程序的最后调用了该方法,并且结束了当前的conversation):

 

   @End

   public void uploadPicture()

   {

      ……

 

      new RunAsOperation() {

         public void execute() {

            identityManager.createUser(username, password);

            identityManager.grantRole(username, "user");          

         }       

      }.addRole("admin").run();

     

     ……

   

      // 登录用户

      identity.setUsername(username);

      identity.setPassword(password);

      identity.login();

   }

 

RunAsOperation 使用较高的优先级来执行特殊的操作。在这种情况下,创建一个新用户时,使用addRole()方法,强迫当前的用户具有管理员权限,它临时授权给这个特殊的RunAsOperation的作用域。在该方法的最后我们看到该用户用在注册时提供的用户名和密码进行登陆。

 

认证

 

以前,Seam安全管理在认证过程中需要调用一个'authenticator' 组件,该组件用来装入用户的角色。虽然这个认证模型依然被支持,但使用IdentityManager 来认证用户实际上更有意义,因为它提供了不需要额外代码的认证能力。记住,使用IdentityManager 来认证你的用户,完全删除在components.xml文件的配置identity 组件的'authenticate-method'属性:

 

    <!-- The old way of authenticating, using an authenticator component -->

    <!--security:identity authenticate-method="#{authenticator.authenticate}"/-->

   

    <!-- The new way to authenticate, using IdentityManager (you don't actually need to   include this element, since it has no attributes now) -->

    <security:identity/>

 

用户管理视图

 

如果你使用管理员帐号登陆 SeamSpace (username/password: demo/demo),你现在会在页面的顶部看到一个'Security' 连接. 点击这个连接显示管理用户和角色的两个选项:

Seam安全升级 - *工* - 要有光,于是就有了光

 

点击第一个连接 'Manage Users' ,带你进入用户管理屏幕:

Seam安全升级 - *工* - 要有光,于是就有了光

从这儿,你可以添加、修改和删除用户。点击 'new user' 显示用户详细资料屏幕,你可以给一个新用户输入详细资料:

Seam安全升级 - *工* - 要有光,于是就有了光

点击 save 会保存新用户,返回到用户管理屏幕,在列表中我们现在可以看到新创建的用户:

Seam安全升级 - *工* - 要有光,于是就有了光

现在让我们来看看角色管理, 点击'Manage Roles'( 从页面的顶端的Security链接)会带我们到角色管理屏幕:

Seam安全升级 - *工* - 要有光,于是就有了光

我们得到一个定义的应用程序的全部角色的列表。点击 ' new role '到角色详细资料屏幕,你可以创建一个新角色:

Seam安全升级 - *工* - 要有光,于是就有了光

这个屏幕比用户屏幕简单,允许你输入新角色名和指定用户组(角色可以是其它角色的成员)。点击save保存,返回的角色管理界面,我们能看到新的角色:

Seam安全升级 - *工* - 要有光,于是就有了光

 

 

这差不多结束了我们对新的身份管理(identity management)特性的高度概括浏览,那么就让我们转到权限管理(permission management)。

 

权限管理(Permission Management)

 

尽管 Identity Management 提供了一致的API来管理用户帐户,我们依然需要一个方式来管理用户许可(Permission)。从前一个版本以来,在Seam 2.1.0中整个的授权(authorization)特性都进行重大修订。替代了需要开发者继承一个内建的 Identity 组件来实现自定义的许可(Permission)检查,在Seam 2.1.0中提供了一个热插拔系统,允许你注册你自己的许可(Permission)解析器而不用覆盖任何内建的行为。下面图表展示了这一切是如何配合在一起:

Seam安全升级 - *工* - 要有光,于是就有了光

 

 

 

根据上述图, Identity 现在使用 PermissionMapper 映射许可(Permission)检查到一个特别的ResolverChain,紧接着它可以配置一个或多个PermissionResolvers。Seam提供了 RuleBasedPermissionResolver (用来解析基于角色的许可检查) 和 PersistentPermissionResolver (用来执行基于持久化库中的许可库的检查,比如一个数据库)。如果你的应用程序需要自定义安全,实现你自己的PermissionResolver 也是相当简单的。

 

我认为在进行下一步前,我们实际上应该定义许可(Permission)究竟是什么。在Seam世界中,许可(Permission)有三个方面:

 

    * A target, 用某种方式被运作的对象

    * An action,对target执行预期的动作

    * A recipient, 被赋予了权力的对target执行指定action的用户或角色实体

   

   

Seam安全升级 - *工* - 要有光,于是就有了光

 

   

许可(Permission)检查的对象是PermissionMapper,用来判断应该使用那个ResolverChain来执行检查。这样可以实现对不同类型的对象配置不同的PermissionResolvers。例如:你可能希望仅用RuleBasedPermissionResolver对Customer对象的执行许可(Permission)检查,你也可以使用PersistentPermissionResolver对Invoice对象上的执行许可(Permission)检查。PermissionMapper容易支持这种级别的灵活性。

 

让我们看看操作这个东西。

 

持久化许可

 

SeamSpace例子允许用户上传图象到他们的个人资料。然后其他用户可以浏览这些资料查看这些图象:

Seam安全升级 - *工* - 要有光,于是就有了光

Seam安全升级 - *工* - 要有光,于是就有了光

 

现在,比方说有一些图象你想设为私有的,其他一些图象你想仅显示给你的朋友。点击你图象下的挂锁图标,会打开图象的许可(Permission)管理屏幕:

Seam安全升级 - *工* - 要有光,于是就有了光

 

在这个屏幕,我们可以看到那些用户和角色现在有查看这个图象的许可。在这个特殊例子中,我们看到仅有你的朋友有查看该图象的许可。现在,比方说我们决定让该站点的任何用户都可以查看这张特殊图象。我们可以点击'new permission'按钮进行授权,会带我们进入许可详细资料屏幕:

Seam安全升级 - *工* - 要有光,于是就有了光

 

在这里,我们可以授予指定的许可动作给选择的角色或个别用户(在此情况下,仅列有你的朋友)。我们希望允许所有的用户可以查看这个图象,所以我闪从角色列表中选择‘user’,然后点击‘view’多选框。点击save按钮保存设置,然后返回到许可管理屏幕,在这里我们可以看到我们的新的许可:

Seam安全升级 - *工* - 要有光,于是就有了光

 

 

在我们授予一个新的许可时,究竟发生了什么呢?我们看看在这个情景后使用的组件,ImagePermission。 这里是相关的代码:

 

@Name("imagePermission")

@Scope(CONVERSATION)

public class ImagePermission implements Serializable

{

  ……

   @In PermissionManager permissionManager;

   @In PermissionSearch permissionSearch;

 

   private MemberImage target;  

   private Principal recipient;

 

   @SuppressWarnings("unchecked")

   @Begin(nested = true)

   public void createPermission() {

      target = (MemberImage) permissionSearch.getTarget();    

    ……

   }

 

   public void applyPermissions() {

     

……

  

      List<Permission> permissions = new ArrayList<Permission>();

    

      for (String role : selectedRoles)

      {

         Principal r = new Role(role);

         for (String action : selectedActions)

         {          

            permissions.add(new Permission(target, action, r));

         }

      }

     

      for (Member friend : selectedFriends)

      {

         MemberAccount acct = (MemberAccount) entityManager.createQuery(

               "select a from MemberAccount a where a.member = :member")

               .setParameter("member", friend)

               .getSingleResult();

Principal p = new SimplePrincipal(acct.getUsername());

       

         for (String action : selectedActions)

         {

            permissions.add(new Permission(target, action, p));

         }

      }

     

      permissionManager.grantPermissions(permissions);

 

      Conversation.instance().endBeforeRedirect();

   }

 

  ……

}

 

根据上面的代码中我们可以看到ImagePermission 是一个对话作用域(conversation-scoped)组件。实事上,它的功能被实现在一个嵌套的对话(conversation)作用域内,对同一个目标(target)对象,允许同时打开多个 'new permission' 窗口。我们也可以看到使用@In 注解注入了PermissionManager组件到我们的组件。

 

 

那么createPermission()方法开始了我们的嵌套的对话 (感谢 @Begin(nested = true)注释),并且存储了一个引用给target对象。那么当用户点击save按钮的那时,在分配了想得到的许可后,applyPermissions()方法被调用,其构建了一个被授权的Permission对象的列表。在该函数内,用授权了的许可列表permissions为参数调用了PermissionManager.grant()。让我们花些时间详细地审查PermissionManager (Permission Management API的核心)。

 

 

PermissionManager组件

 

就像 IdentityManager 用来处理用户和角色操作一样,PermissionManager 被设计为来操作许可。它提供了API来允许授权和激活许可,或则一个target对象的列表。我们来看看一些方法:

 

    * listPermissions(String target, String action)

    * listPermissions(Object target)

    * grantPermission(Permission permission)

    * grantPermissions(List<Permission> permissions)

    * revokePermission(Permission permission)

    * revokePermissions(List<Permission> permissions)

 

就像 IdentityManager 需要一个 IdentityStore 来保存数据, PermissionManager 也需要一个 PermissionStore 来和持久化存储打交到。Seam 只提供了一个 PermissionStore 实现 - JpaPermissionStore, 可以通过JPA来和数据库存在互操作。在理论上也可以把许可信息保存在LDAP目录中,或则一个普通文件中,但是大多数情况下还是保存在一个数据库中的。

 

现在来看看SeamSapce例子中 AccountPermission entity bean的代码,下面的代码为了排版被删节了:

 

 

@Entity

public class AccountPermission implements Serializable

{

   // snip field declarations, etc

    

   @PermissionUser @PermissionRole

   public String getRecipient() { return recipient; }

   public void setRecipient(String recipient) { this.recipient = recipient; }

 

   @PermissionTarget public String getTarget() { return target; }

   public void setTarget(String target) { this.target = target; }

 

   @PermissionAction

   public String getAction() { return action; }

   public void setAction(String action) { this.action = action; }

 

   @PermissionDiscriminator

   public String getDiscriminator() { return discriminator; }

   public void setDiscriminator(String discriminator) { this.discriminator = discriminator; }

}

 

再次,我们注意到使用了一些特殊的注解。这里我告诉你,许可既可以指定到用户上也可以指定到角色上。这就意味着可以把许可信息保存的分开的表格中,这从性能角度来说,把他们保存在单个表中,使用一个辨别者列(discriminator column)来区分他们,可能看起来更合适。因此在上面的代码中,我们看到getDiscriminator() 使用了@PermissionDiscriminator注解,表明该列用来判断许可是应用到用户上还是角色上。

 

接下来,是用于配置一个存储许可的实体的特殊注解:

 

    * @PermissionUser – 指派包含许可接收者名字的字段(为用户指派许可)。

    * @PermissionRole – 同上,只是为角色指派许可。

   * @PermissionTarget – 包含一个唯一的标识符字符串,标识单个对象实例。另外,可以包含一个类名或者包含一个指定更普通的权限的任意字符串。

    * @PermissionAction – 包含一个动作列表,接收者可能在目标对象上执行。

    * @PermissionDiscriminator – 让看前段内容。

 

 

刚指出- 许可管理的功能只是为了持久化许可的管理。大多数情况下,你希望基于业务逻辑授予许可,例如,用户总是应该有权力查看和管理他们自己的图象。这些许可类型由Seam的基于规则的安全来处理,后面有更详细的讨论。


基于规则的权限

 

Seam一直支持建立在Drools上的基于规则的安全模型,所以这并不是一个真正的新功能。通过了解它如何在我们的例子应用程序——SeamSpace的作用域中的使用,让我们重新温习它。

 

继续就图象安全这个话题,除了在上面已经讨论过的关于持久化权限之外,在处理用户图象的时候,我们需要有一些适当的安全规则是显而易见的。在默认时,查看所有用户的图象受安全约束的限制,这意味着在SeamSpace中,如果你想查看一个用户的图象,这样做或那样做,你必须被明确地被授与许可才行(通过一个持久化许可或者通过安全规则授与)。请记住,我们特别需要允许以下情况:

 

    *用户对他们自己的图象应该拥有授予和废除许可的权力

    *用户应该被允许删除他们自己的图象。

    *用户的个人资料图象(例如,一个用户的主图象。他们的“化身”)应该总被另外的人看得见

    *用户应该总是被允许查看他们自己的图象 (当然)

    *带有'friend' 许可的用户的图象应该被用户的朋友看得见(更关注这点)

 

让我们详细地考查这些规则。首先,用户对他们自己的图象应该拥有授予和废除许可的权力。这是相当简单的,作为两个独立的规则被实现。在与对象许可一起工作时,Seam会插入一个PermissionCheck对象到包含了两个许可对象的Drools的工作内存中,是'seam.grant-permission' 还是 'seam.revoke-permission'动作,依赖于用户试图做什么。(根据是否调用PermissionManager.grantPermission() 或PermissionManager.revokePermission())。当前已被认证的用户的MemberAccount实例始终存在于工作内存中,因此下面的规则实际上是说: '如果我们正在处理许可检查的MemberImage是属于当前用户的,那么就授予许可 ':

 

rule GrantImagePermissions

    no-loop

    activation-group "permissions"

when

    acct: MemberAccount()

 

    image: MemberImage(mbr : member -> (mbr.memberId.equals(acct.member.memberId)))

 

    check: PermissionCheck(target == image, action == "seam.grant-permission", granted == false)

 

then

    check.grant();

end

 

rule RevokeImagePermissions

    no-loop

    activation-group "permissions"

when

    acct: MemberAccount()

    image: MemberImage(mbr : member -> (mbr.memberId.equals(acct.member.memberId)))

    check: PermissionCheck(target == image, action == "seam.revoke-permission", granted == false)

then

    check.grant();

end

 

接下来,我们也需要一个规则,允许用户删除他们自己图象。相似于最初的两个规则,我们也检查当前已被认证的用户他删除的图象的所有者是否是他自己,如果是就授予许可:

 

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

 

我们的查看的个人资料图象的规则是有点不同。在这里我们简单的测试——被查看图象的所有者是否是成员的个人资料的图象 (例如image.getMember().getPicture() == image):

 

rule ViewProfileImage

    no-loop

    activation-group "permissions"

when

    image: MemberImage()

    check: PermissionCheck(target == image, action == "view", granted == false)

    eval( image.getMember().getPicture() == image )

then

    check.grant();

end

 

还有,用户应该总是可以查看他们自己的图象。这个许可检查类似于前几个规则,在那里我们简单的检查图象的所有者是否是当前已被认证的用户:

 

rule ViewMyImages

    no-loop

    activation-group "permissions"

when

    acct: MemberAccount()

    image: MemberImage(mbr : member -> (mbr.memberId.equals(acct.member.memberId)))

    check: PermissionCheck(target == image, action == "view")

then

    check.grant();

end

 

 

条件角色

 

最后,为了允许我们的朋友查看我们的图象,我们需要有一个特殊的规则。先前我们看到了如何给‘friends’角色授权,然而取决于上下文的 ‘friend’可能有不同的意思。可以说,在系统中的单个用户都可能是另外某人的‘friend’,我们如何判断在查看图象的人中谁是一个‘friend’呢?在这里条件角色引进来了——这些角色是特殊的,并且不能明确地授予给用户。

 

当安全(security)API对一个对象进行许可检查时,许可管理器(permission manager)通知安全(security)API一个条件角色已被授予许可,需要执行一个特殊的基于规则(rule-based)的检查来鉴定当前用户是否确实有这个角色,但仅在许可检查的上下文中检查。为达到这个,象每个普通的PermissionCheck对象一样被插入到包含target和action的工作内存中,然而另外一个RoleCheck对象也会被插入,包含被鉴定的条件角色的名字。使用这种方法,我们可以编写一个安全规则来确定是否授予条件角色:

 

rule FriendViewImage

    no-loop

    activation-group "permissions"

when

    acct: MemberAccount()

    image: MemberImage(mbr : member -> (mbr.isFriend(acct.member)))

    PermissionCheck(target == image, action == "view")

    role: RoleCheck(name == "friends")

then

    role.grant();

end

 

这个规则检查当前已认证的用户是否在目标图象所有者的朋友列表中。如果在,那么角色临时授予这个特殊的许可检查。由于不切实际或设计受限,在指派复杂的安全规则到不一定需要有自己的角色或组的动态用户组(在这个情况下,是一个用户的朋友列表)时,这具有更大的灵活性。

 

强类型安全注释

 

最后,作为结束让我们看看一些新的安全注解。为了使Seam安全看起来更'Web Beansy',我们引入了一些用于限制组件方法的类型安全注解。通过使用元注释,我们可以提供一组安全注解,把安全限制应用到函数或它的参数。Seam提供了一组开箱即用的标准的CRUD注解 (@Insert, @Read, @Update, @Delete) ,并且添加你自己的是小事一桩。看看下面的例子:

 

@Begin @Insert(Customer.class)

public void createCustomer() {

 

这个注解防止用户调用createCustomer(),除非他们有插入新customer对象的许可。同样的,我们可以注解一个方法的参数:

 

public void updateCustomer(@Update Customer customer) {

 

创建你自己的安全注释和用@PermissionCheck元注释你的注释一样简单。例如说,你希望创建一个叫‘Promote’的新许可。注解可以容易地被定义,象这样:

 

@Target({METHOD, PARAMETER})

@Documented

@Retention(RUNTIME)

@Inherited

@PermissionCheck

public @interface Promote {

     Class value() default void.class;

}

 

定义注释后,就可以被立即使用:

 

public void promoteStaff(@Promote Staff person) {

 

如果写一个基于规则的许可,该规则可能是这个样子:

 

rule PromoteStaffMember

    no-loop

    activation-group "permissions"

when

    acct: MemberAccount()

    Role(name == 'admin')

    staff: Staff()

    check: PermissionCheck(target == staff, action == "promote")

then

    check.grant();

end

 

许可的动作成为注解名字的小写名称。它就是这么简单!尽管我们仍然支持基于表达式的安全检查的遗留注解@Restrict,我建议各位试试新的类型安全注解,至少它对编译期安全提供了简单的限制。

 

结束

 

到这里就结束了在JBoss Seam中的新安全功能的概述。这里有一些参考链接:

 

JBoss Seam Community Site (下载,文档,论坛) - http://www.seamframework.org/

 

JBoss Home Page - http://www.jboss.org

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值