使用 WebSphere Application Server Community Edition 配置 EJB 应用程序安全性

由于 WebSphere® Application Server Community Edition v2.x 中引入了凭据库、Run-as 主体和缺省主体,因此其安全性配置与之前版本有所不同。本文将介绍如何使用 WebSphere Application Server Community Edition V2.x 配置 EJB 应用程序安全性。您可以通过本文熟悉各种安全相关的注释、EJB 部署描述符中的元素以及 Community Edition 特定的部署计划,用于保护会话、实体和消息驱动的 EJB 的安全。

Vamsavardhana Reddy Chillakuru, 顾问软件工程师, EMC

Vamsavardhana Reddy Chillakuru 又名 Vamsi,是位于印度班加罗尔的 IBM India Software Labs 的一名顾问软件工程师。他是 Apache Geronimo 和 Apache Tuscany 的参与者,也是 IBM WebSphere Application Server Community Edition Level 3 Support Team 的成员。他分别于 1994 年和 1996 年获得了位于印度加尔各答的印度统计研究所 (Indian Statistical Institute) 的统计学学士学位(荣誉)和统计学硕士学位。


developerWorks 投稿作者

Manu T. George, 软件工程师, EMC

Manu T. George 是位于印度班加罗尔的 IBM India Software Labs 的一位软件工程师。他是 Apache Geronimo 和 Apache OpenEJB 项目的参与者,也是 IBM WebSphere Application Server Community Edition Level 3 Support Team 的成员。他在 2001 年获得了 College of Engineering Trivandrum 的应用电子学士学位。



2009 年 5 月 13 日

引言

保护 Enterprise Java Bean (EJB) 的安全是 Java EE 应用程序安全性最重要的方面。IBM WebSphere Application Server Community Edition v2.1(以下称为 Community Edition)是免费的 Java Enterprise Edition 5.0 (Java EE5) 认证应用服务器,基于为保护 EJB 应用程序而提供的 Apache Geronimo v2.1。Community Edition 使用 Apache OpenEJB 作为 EJB 容器。EJB 的身份验证和授权配置方法为:在部署描述符中定义安全角色,每个方法将依据部署描述符执行。然后将安全角色映射到 Community Edition 特定部署计划中的主体。

在本文中,我们将讨论可使用 Community Edition 来保护会话、实体和消息驱动的 EJB 的不同选项。我们还将开发一些 EJB 应用程序来演示各种安全性配置。为了遵循本文所述进行操作,您需要 WebSphere Application Server Community Edition v2.1。


没有安全性的简单 EJB 应用程序

接下来让我们看一个未配置安全性的简单 EJB 应用程序。此应用程序包含一个 EJB2.1 无状态会话 Bean 和一个 EJB3 无状态会话 Bean。清单 1 显示了此应用程序的部署描述符 ejb-jar.xml

清单 1. 显示 Bean 定义的部署描述符
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar ...>
    <display-name>SimpleEjbApp</display-name>
    <enterprise-beans>
        <session>
            <description>Simple Session Bean</description>
            <display-name>SimpleSessionBean</display-name>
            <ejb-name>SimpleSessionBean</ejb-name>
            <home>simple.ejb21.SimpleServiceHome</home>
            <remote>simple.ejb21.SimpleService</remote>
            <local-home>simple.ejb21.SimpleServiceLocalHome</local-home>
            <local>simple.ejb21.SimpleServiceLocal</local>
            <ejb-class>simple.ejb21.SimpleServiceBean</ejb-class>
            <session-type>Stateless</session-type>
            <transaction-type>Container</transaction-type>
        </session>
    </enterprise-beans>
</ejb-jar>

请注意,此部署描述符仅仅显示了一个 Bean 定义,即 SimpleSessionBean。我们通过在 Bean 类中使用注释来指定 EJB3 会话 Bean 的部署描述符。清单 2 显示了 Simple3ServiceBean 的 Bean 类。

清单 2. Simple3ServiceBean 类
import  javax.ejb.Stateless;
...

@Stateless
public class Simple3ServiceBean implements Simple3Service {

	  public String commonMethod(){
		  ...
	  }

	  public String userMethod(){
		  ...
	  }

	  public String adminMethod(){
		  ...
	  }
	  
	  public String noaccessMethod(){
		  ...
	  }
}

在此示例中,@Stateless 注释指定 Simple3ServiceBean 是无状态会话 Bean。

两个会话 Bean SimpleSessionBeanSimple3ServiceBean 没有配置任何安全性,因此所有方法的访问都没有限制。此应用程序的部署计划 openejb-jar.xml 如清单 3 中所示。

清单 3. 简单 EJB 应用程序的部署计划
<openejb-jar ...>
    <environment>
        <moduleId>
            <groupId>dw</groupId>
            <artifactId>simple-ejb-app</artifactId>
            <version>1.0</version>
            <type>jar</type>
        </moduleId>
        <dependencies/>
        <hidden-classes/>
        <non-overridable-classes/>
    </environment>
    <enterprise-beans>
        <session>
            <ejb-name>SimpleSessionBean</ejb-name>
            <jndi-name>ejb/SimpleSessionBean</jndi-name>
        </session>
    </enterprise-beans>
</openejb-jar>

此 EJB 应用程序部署在 ID 为 dw/simple-ejb-app/1.0/jar 的模块下。请注意,在此应用程序中,我们将使用 moduleId 元素下的信息对 EJB 进行映射,在本文稍后将看到这一点。


用于演示 EJB 安全性的简单 Web 应用程序

在本文中,我们使用一个简单的 Web 应用程序来演示 EJB 安全性。此 Web 应用程序使用两个安全角色,即“bank”和“customer”。此 Web 应用程序包括一个无访问限制的主页和两个可由“bank”和“customer”角色访问的安全页。每个页面都访问 SimpleSessionBeanSimple3ServiceBean 方法,并显示此方法的结果或方法调用所引发的任何异常。清单 4 显示了 web.xml 部署描述符。

清单 4. 简单 Web 应用程序的部署描述符
<web-app ...>
    <display-name>simple-web-app</display-name>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
        ...
    </welcome-file-list>

    <login-config>
        <auth-method>FORM</auth-method>
        <realm-name>Not required for FORM auth</realm-name>
        <form-login-config>
            <form-login-page>/login/login.jsp</form-login-page>
            <form-error-page>/login/loginerror.jsp</form-error-page>
        </form-login-config>
    </login-config>

    <security-role>
        <role-name>customer</role-name>
    </security-role>
    <security-role>
        <role-name>bank</role-name>
    </security-role>
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Customer</web-resource-name>
            <url-pattern>/customer/*</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
        </web-resource-collection>
        <auth-constraint>
            <role-name>customer</role-name>
                 <!-- This comes from the security-role element -->
        </auth-constraint>
    </security-constraint>
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Bank</web-resource-name>
            <url-pattern>/bank/*</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
        </web-resource-collection>
        <auth-constraint>
            <role-name>bank</role-name> 
                   <!-- This comes from the security-role element -->
        </auth-constraint>
    </security-constraint>

    <ejb-ref>
        <ejb-ref-name>ejb/SimpleService</ejb-ref-name>
        <ejb-ref-type>Session</ejb-ref-type>
        <home>simple.ejb21.SimpleServiceHome</home>
        <remote>simple.ejb21.SimpleService</remote>
    </ejb-ref>
    <ejb-ref>
        <ejb-ref-name>ejb/Simple3Service</ejb-ref-name>
        <ejb-ref-type>Session</ejb-ref-type>
        <remote>simple.ejb3.Simple3Service</remote>
    </ejb-ref>
    ....
</web-app>

此 Web 应用程序使用 login-config 元素中定义的表单身份验证。两个安全角色使用 security-role 元素定义。我们使用 security-constraint 标记配置资源集合的安全权限。在此示例中,bank 角色中的用户可以访问模式为 /bank/* 的 URL,而 customer 角色中的用户可以访问模式为 /customer/* 的 URL。

我们使用 ejb-ref 元素声明 EJB 引用,以通过远程 Home 和远程接口访问 Bean。在此示例中,我们定义了两个 EJB 引用,即 ejb/SimpleServiceejb/Simple3Service。请注意,没有为 Simple3Service Bean(EJB3 会话 Bean)定义 Home 接口。可以使用 ejb-local-ref 元素声明用于通过本地 Home 和本地接口访问 Bean 的 EJB 引用。

geronimo-web.xml Web 应用程序部署计划中,我们将 web.xml 中定义的 EJB 引用映射到特定的 EJB,并将安全角色映射到主体。清单 5 显示了示例 Web 应用程序的部署计划。

清单 5. 简单 Web 应用程序的部署计划
<web-app ...>
    ...
    <context-root>simple-web-app</context-root>
    <nam:ejb-ref xmlns:nam="http://geronimo.apache.org/xml/ns/naming-1.2">
        <nam:ref-name>ejb/SimpleService</nam:ref-name>
        <nam:pattern>
            <nam:groupId>dw</nam:groupId>
            <nam:artifactId>simple-ejb-app</nam:artifactId>
            <nam:version>1.0</nam:version>
            <nam:name>SimpleSessionBean</nam:name>
        </nam:pattern>
    </nam:ejb-ref>
    <nam:ejb-ref xmlns:nam="http://geronimo.apache.org/xml/ns/naming-1.2">
        <nam:ref-name>ejb/Simple3Service</nam:ref-name>
        <nam:pattern>
            <nam:groupId>dw</nam:groupId>
            <nam:artifactId>simple-ejb-app</nam:artifactId>
            <nam:version>1.0</nam:version>
            <nam:name>Simple3ServiceBean</nam:name>
        </nam:pattern>
    </nam:ejb-ref>
    ...
    <security-realm-name>SampleSecurityRealm</security-realm-name>
    <app:security ...>
        ...
        <sec:role-mappings>
            <sec:role role-name="customer">
                <sec:principal name="UserGrp" 
                    class="o.a.g.s.r.providers.GeronimoGroupPrincipal"/>
                ...
            </sec:role>
            <sec:role role-name="bank">
                <sec:principal name="AdminGrp" 
                    class="o.a.g.s.r.providers.GeronimoGroupPrincipal"/>
            ...
            </sec:role>
        </sec:role-mappings>
    </app:security>
</web-app>

我们使用 ejb-ref 元素将 EJB 引用映射到特定的 EJB。在本例中,请注意 ejb-ref 下的 ref-name 元素中的值与 web.xml 中的 ejb-ref 下的 ejb-ref-name 元素(请参见清单 4)匹配。类似地,我们使用 ejb-local-ref 元素将 EJB 本地引用映射到特定 EJB。

security-realm-name 元素用于配置针对其对用户进行身份验证的安全领域。在此示例中,我们使用的领域名称为 SampleSecurityRealm

安全角色映射到 security 元素内的 role-mappings 元素下的主体。我们对每个要映射的安全角色使用一个 role 元素。安全角色的名称在 role 元素的 role-name 属性中指定。我们使用 role 元素内的 principal 元素指定映射到安全角色的主体。在此示例中,我们将 bank 角色映射到名为 AdminGrpGeronimoGroupPrincipal,将 customer 角色映射到名为 UserGrpGeronimoGroupPrincipal。有关主体到角色的映射的详细信息,请参见“参考资料”部分提供的在 WebSphere Application Server Community Edition V2.0 中配置 Web 应用程序安全性的链接。


运行没有 EJB 安全性的示例应用程序

现在我们将运行访问 EJB 应用程序中的 EJB 的简单 Web 应用程序。请注意,目前我们尚未给 EJB 配置安全性。在继续之前,请下载示例存档,并将其提取到您选择的目录,(我们将会把此目录称为 <SAMPLES_HOME>),并将 dw_users.properties 和 dw_groups.properties 文件复制到 <WASCE_HOME>/var/security 目录(<WASCE_HOME> 指代 Community Edition 安装目录)。

要运行示例应用程序,请按下列步骤操作:

  1. 启动 Community Edition,并在浏览器中打开 http://localhost:8080/console/
  2. user name 字段输入 system,在 password 字段输入 manager。单击 Login,将随即在管理控制台中打开 Welcome 页。
  3. 导航到 Deploy New Portlet。
  4. Plan 字段下,导航到 <SAMPLES_HOME> 并选择文件 SampleSecurityRealm-plan.xml
  5. 单击 Install,以完成安全领域部署。
  6. Archive 字段下,导航到 <SAMPLES_HOME> 并选择 simple-ejb-app.jar
  7. 单击 Install,以完成 EJB 应用程序部署。
  8. 在 Archive 字段下,导航到 <SAMPLES_HOME> 并选择 simple-web-app.war
  9. 单击 Install,以完成 Web 应用程序部署。
  10. 访问 http://localhost:8080/simple-web-app,会显示图 1 中所示的屏幕。
    图 1. Web 应用程序主页
    Web 应用程序主页的屏幕截图

    User Info 部分显示 Web 应用程序中当前登录的用户的 UserPrincipalUserRolesEJB2.1 部分显示调用 SimpleSessionBean 中的每个方法的结果。EJB3 部分显示调用 Simple3ServiceBean 中的每个方法的结果。由于我们尚未登录到应用程序,因此 UserPrincipalUserRole 为空。由于没有为任何 EJB 方法配置安全性,因此所有方法都可访问,并作为未经身份验证的用户被调用。

  11. 访问 http://localhost:8080/simple-web-app/bank。使用用户 dwadmin 和密码 admin 登录,如图 2 中所示。
    图 2. Web 应用程序银行页
    Web 应用程序银行页的屏幕截图

    请注意,UserPrincipal 显示已登录用户为 dwadminUserRolebank。由于没有为任何 EJB 方法配置安全性,因此所有方法都可访问,并以未经身份验证的用户进行调用。

到目前为止,我们看到的都是 Web 应用程序使用无限制的方法访问调用 EJB2.1 和 EJB3 会话 Bean。接下来,我们将配置 EJB2.1 和 EJB3 会话 Bean 的方法权限。


EJB2.1 部署描述符中的安全性配置

EJB 应用程序对 Bean 级别和方法级别的安全性使用基于角色的安全性。在此部分,我们将了解如何定义安全角色和配置方法权限。

定义安全角色

我们在部署描述符文件 ejb-jar.xml 中使用 assembly-descriptor 下的 security-role 元素定义安全角色。role-name 元素指定安全角色的名称。清单 6 显示了本文中使用的示例应用程序之一的 ejb-jar.xml 的摘要。

清单 6. 显示角色定义的组装描述符
<ejb-jar ...>
    <display-name>SimpleEjbApp</display-name>
    <enterprise-beans>
    ...
    </enterprise-beans>
    <assembly-descriptor>
      <security-role>
         <description>User role</description>
         <role-name>ejbuser</role-name>
      </security-role>
      <security-role>
         <description>Administrator role</description>
         <role-name>ejbadmin</role-name>
      </security-role>
      ...
    </assembly-descriptor>
</ejb-jar>

在此示例中,应用程序定义了两个角色,即 ejbuserejbadmin

配置方法权限

为了配置安全标识来调用 EJB 中的方法,我们在 ejb-jar.xmlenterprise-beans 下的 Bean 定义中使用了 security-identity 元素。我们通过在 ejb-jar.xml 中的 assembly-descriptor 下使用 method-permission 元素定义 Bean 方法权限。清单 7 显示了用于配置 SimpleSessionBean 的方法权限的 EJB 应用程序部署描述符的片段。

清单 7. EJB 应用程序的部署描述符
<ejb-jar id="SimpleEjbAppWithSecurity" ...>
    <enterprise-beans>
        <session>
            ...
            <ejb-name>SimpleSessionBean</ejb-name>
            ...
            <security-identity>
                <use-caller-identity />
            </security-identity>
        </session>
        ...
    </enterprise-beans>
    <assembly-descriptor>
        <security-role>
        ...
        </security-role>
        <method-permission>
            <role-name>ejbuser</role-name>
            <method>
                <ejb-name>SimpleSessionBean</ejb-name>
                <method-name>userMethod</method-name>
            </method>
        </method-permission>
        <method-permission>
            <role-name>ejbadmin</role-name>
            <method>
                <ejb-name>SimpleSessionBean</ejb-name>
                <method-name>adminMethod</method-name>
            </method>
        </method-permission>
        <method-permission>
            <unchecked/>
            <method>
                <ejb-name>SimpleSessionBean</ejb-name>
                <method-name>commonMethod</method-name>
            </method>
        </method-permission>
        <method-permission>
            <role-name/>
            <method>
                <ejb-name>SimpleSessionBean</ejb-name>
                <method-name>noaccessMethod</method-name>
            </method>
        </method-permission>
    </assembly-descriptor>
</ejb-jar>

接下来我们逐一介绍一下在本示例中使用的各个标记:

  • security-identity 元素指定用于调用 SimpleSessionBean 中的方法的安全标识。
  • use-caller-identity 元素指定将调用方的安全标识用作执行企业 Bean 的方法的安全标识。另一个选项是使用 run-as 元素来指定用于调用 Bean 的方法 Run-as 标识(请参见本文后面的相关内容)。
  • method-permissionmethod 元素指定 Bean 中一个或多个方法的安全权限。
  • 您可以使用一个或多个 role-name 元素指定权限,允许访问此方法的每个角色有一个对应的此元素。单个空白 role-name 元素表明角色对方法没有访问权限。使用 unchecked 元素指定无限制访问权限。
  • 方法的 ejb-name 元素指定 EJB 的名称。method-name 元素指定方法的名称。method-name“*” 值与 EJB 中的所有方法对应。
  • 特定的 method-name 与该名称的所有方法对应,包括任何重载方法。要指定重载方法中的特定方法,请使用 method-params 元素。
  • 您可以在 Bean 的 Home、Remote、Local 和 LocalHome 接口中为方法配置 method-permission。要在特定接口中指定方法的权限,请使用具有对应值的方法下的 method-intf 元素。

在清单 7 中的 SimpleSession Bean 中,我们配置了以下内容:

  • userMethod 方法可由 ejbuser 角色访问。
  • adminMethod 方法可由 ejbadmin 角色访问。
  • commonMethod 具有无限制的访问。
  • noaccessMethod 不能由任何角色访问。

在 EJB3 Bean 中使用注释进行安全配置

在 EJB3 企业 Bean 中,可以使用安全注释配置 Bean 级别和方法级别的安全性。在此部分,我们将讨论各个安全注释(@DeclareRoles@DenyAll@PermitAll@RolesAllowed)以及它们如何影响安全性配置。请注意,我们将在本文后面讨论 @RunAs 注释。

定义安全角色

通过对 Bean 类使用 @DeclareRoles 注释定义安全角色,如清单 8 中所示。

清单 8. 带 @DeclareRoles 注释的 Simple3ServiceBean
@Stateless
@DeclareRoles({"ejb3user", "ejb3admin"})
public class Simple3ServiceBean implements Simple3Service {
...
}

在此示例中,Simple3ServiceBean 定义了两个安全角色,即 ejb3userejb3admin

配置方法权限

使用 @DenyAll@PermitAll@RolesAllowed 注释为企业 Bean 定义权限。@PermitAll@RolesAllowed 注释可以应用于 Bean 类和 Bean 方法,而 @DenyAll 注释只能应用于 Bean 方法。应用于类的注释适用于类中的所有方法。应用于方法的注释覆盖应用于类的任何注释。清单 9 显示了 Simple3ServiceBean 类。

清单 9. 带安全注释的 Simple3ServiceBean 类
@Stateless
@DeclareRoles({"ejb3user", "ejb3admin"})
public class Simple3ServiceBean implements Simple3Service {

	  @Resource
	  private SessionContext ctx;
	
        @PermitAll
	  public String commonMethod() {
		  return logCall("commonMethod");
	  }

        @RolesAllowed({"ejb3user"})
	  public String userMethod() {
		  return logCall("userMethod");
	  }

        @RolesAllowed({"ejb3admin"})
	  public String adminMethod() {
		  return logCall("adminMethod");
	  }
	  
        @DenyAll
	  public String noaccessMethod() {
		  return logCall("noaccessMethod");
	  }
	  
	  private String logCall(String method) {
              ...
		  return msg;
	  }
}

此示例定义了以下访问配置:

  • commonMethod 具有无限制访问。
  • userMethod 可由 ejb3user 角色访问。
  • adminMethod 可由 ejb3admin 角色访问。
  • noaccessMethod 不能由任何角色访问。

EJB 部署计划中的安全角色映射

EJB 部署描述符中定义的安全角色和使用 @DeclareRoles 注释定义的角色映射到 EJB 部署计划 openejb-jar.xml 中的主体。清单 10 显示了 EJB 部署计划中的主体到角色的映射。

清单 10. 主体到角色的映射
<security>
    <role-mappings>
        <role role-name="ejbuser">
            <principal class="o.a.g.s.r.providers.GeronimoGroupPrincipal" 
                  name="UserGrp"/>
        </role>
        <role role-name="ejbadmin">
            <principal class="o.a.g.s.r.providers.GeronimoGroupPrincipal" 
                  name="AdminGrp"/>
        </role>
        <role role-name="ejb3user">
            <principal class="o.a.g.s.r.providers.GeronimoGroupPrincipal"
                  name="UserGrp"/>
        </role>
        <role role-name="ejb3admin">
            <principal class="o.a.g.s.r.providers.GeronimoGroupPrincipal" 
                  name="AdminGrp"/>
            <principal class="o.a.g.s.r.providers.GeronimoUserPrincipal" 
                  name="dwuser3"/>
        </role>
    </role-mappings>
</security>

请注意, ejb-jar.xml 中和 @DeclareRoles 注释中的安全角色映射到主体:

  • ejbuser 角色映射到名称为 “UserGrp”GeronimoGroupPrincipal
  • ejbadmin 角色映射到名称为 “AdminGrp”GeronimoGroupPrincipal
  • ejb3user 角色映射到名称为 “UserGrp”GeronimoGroupPrincipal
  • ejb3admin 角色映射到名称为 “AdminGrp”GeronimoGroupPrincipal 和名称为 “dwuser3”GeronimoUserPrincipal

有关主体到角色的映射的详细信息,请参见在 WebSphere Application Server Community Edition V2.0 中配置 Web 应用程序安全性


运行使用 EJB 安全性的示例应用程序

导航到 Deploy New Portlet。

  1. Archive 文件下,导航到 <SAMPLES_HOME> 并选择 simple-ejb-app-w-security.jar
  2. 选择 Redeploy application 选项,并单击 Install
  3. 导航到 Web App WARs Portlet 并启动 dw/simple-web-app/1.0/war
  4. 访问 http://localhost:8080/simple-web-app,会显示图 3 中所示的屏幕。
    图 3. 访问安全 EJB 的 Web 应用程序主页
    访问安全 EJB 的主页的屏幕截图

    请注意,由于用户没有登录到应用程序,只能从主页调用 SimpleSessionBean 中的 commonMethodSimple3ServiceBean (具有无限制访问)。由于 Principal 未得到授权,因此所有其他方法调用都会生成 AccessExceptionEJBAccessException

  5. 访问 http://localhost:8080/simple-web-app/bank/。使用名称 dwadmin 和密码 admin 登录,如图 4 中所示。
    图 4. 访问安全 EJB 的 Web 应用程序银行页
    访问安全 EJB 的银行页的屏幕截图

    由于用户现在登录为 dwadminSubject 包含名称为 dwadminGeronimoUserPrincipal 和名称为 AdminGrpGeronimoGroupPrincipal。根据角色映射,此用户映射到 ejbadminejb3admin 角色。因此用户可以访问 commonMethod(具有无限制访问)和 adminMethod(可由 SimpleSessionBeanejbadmin 角色和 Simple3ServiceBeanejb3admin 角色访问)。userMethodnoaccessMethod 在调用时会生成 AccessExceptionEJBAccessExceptionuserMethod 可由 ejbuser 角色(Simple3ServiceBeanejb3user 角色)访问,noaccessMethod 不能由任何角色访问。

  6. 打开新浏览器窗口并访问 http://localhost:8080/simple-web-app/customer/。使用名称 dwuser3 和密码 user3 登录,如图 5 中所示。
    图 5. 访问安全 EJB 的 Web 应用程序客户页
    访问安全 EJB 的客户页的屏幕截图

    由于用户现在登录为 dwuser3Subject 包含名称为 dwuser3GeronimoUserPrincipal 和名称为 UserGrpGeronimoGroupPrincipal。根据角色映射,此用户映射到 ejbuserejb3userejb3admin 角色。在 SimpleSessionBean 中,用户可以访问 commonMethod(具有无限制访问)和 userMethod(可由 ejbuser 角色访问)。在 Simple3ServiceBean 中,用户可以访问 commonMethod(具有无限制访问)、userMethod(可由 ejb3user 角色访问)和 adminMethod(可由 ejb3admin 角色访问)(请参见 EJB2.1 部署描述符中的安全性配置)。所有其他方法在调用时都会生成 AccessExceptionEJBAccessException


Run-as 主体和缺省主体

除了配置 Bean 的方法的方法权限外,还为 Bean 配置 Run-as 角色,以指定 Bean 用于调用其他 Bean 的安全标识。此配置在 Bean 的方法需要使用其他安全标识(不是调用此方法时使用的标识)调用其他 Bean 时非常有用,例如,无需安全标识即可调用的消息驱动的 Bean 方法调用安全会话 Bean 方法时。在部署计划中配置安全凭据,以构成 Run-as 角色安全性的主体。

在 V2.0 之前的 Community Edition 中,Run-as 主体和缺省主体的安全性配置是使用通过 default-principal 标记和主体的 designated-run-as 属性指定的主体和凭据构造的。从 Community Edition 2.0 开始,源自主体的所有安全性流都登录到安全领域。要使用这些主体,需要提供每个此类主体的登录信息。此登录信息在凭据库中捕获。


凭据库

Community Edition 提供了凭据库的实现,即允许在部署计划中使用 XML 配置凭据库的 SimpleCredentialStoreImpl。清单 11 显示了我们在本文的示例应用程序中使用的凭据库。

清单 1. 凭据库 gbean
<gbean name="SampleCredentialStore" 
      class="o.a.g.s.credentialstore.SimpleCredentialStoreImpl" ...>
    <xml-attribute name="credentialStore">
        <credential-store 
              xmlns="http://geronimo.apache.org/xml/ns/credentialstore-1.0">
            <realm name="SampleSecurityRealm">
                <subject>
                <id>dwuser1-subject</id>
                    <credential>
                        <type>o.a.g.s.credentialstore.NameCallbackHandler</type>
                        <value>dwuser1</value>
                    </credential>
                    <credential>
                        <type>o.a.g.s.credentialstore.PasswordCallbackHandler</type>
                        <value>user1</value>
                    </credential>
                </subject>
                <subject>
                    <id>dwuser2-subject</id>
                    ...
                </subject>
                <subject>
                    <id>dwuser3-subject</id>
                    ...
                </subject>
                <subject>
                    <id>dwadmin-subject</id>
                    ...
                </subject>
            </realm>
            <realm name="AnotherRealm">
                <subject>
                ...
                </subject>
                ...
            </realm>
        </credential-store>
    </xml-attribute>
</gbean>

其中,realm 元素的 name 属性指定用于创建 subjectsecurity-realmsubject 元素下的 id 元素为 subject 指定 ID,可在配置 Run-as 主体和缺省主体时与 security-realm 名称结合使用,详见后文中所述。

subject 下使用 credential 元素配置 security-realm 的登录信息。在清单 11 中,我们使用用户名 dwuser1 和密码 user1 登录到 SampleSecurityRealm 获得了 dwuser1-subject。当前 Community Edition 提供针对名称和密码的回调处理程序,足以使用需要名称和密码进行身份验证的安全领域。为了使用凭据库的其他安全领域(例如,使用数字证书的安全领域),您需要实现所需的回调处理程序。另外,凭据库可以按应用程序配置,或者可以拥有供所有应用程序使用的一个凭据库。


配置应用程序来使用凭据库

由应用程序使用的凭据库在应用程序的部署计划中的安全配置中指定。清单 12 显示了用于本文使用的示例应用程序之一的安全配置。

清单 12. 显示 credential-store-ref 的安全配置
<security>
    <credential-store-ref>
        <name xmlns="http://geronimo.apache.org/xml/ns/deployment-1.2">
             SampleCredentialStore</name>
    </credential-store-ref>
    ...
</security>

在此示例中,credential-store-ref 下的 name 元素指定应用程序使用的凭据库。请注意,此值与清单 11 中所示的凭据库 gbean 的 name 属性匹配。


配置 Run-as 主体和缺省主体

应用程序的 Run-as 主体和缺省主体在应用程序的部署计划的安全配置中指定。如果未为任何角色配置 Run-as 主体,则将转而使用所配置的缺省主体。清单 13 显示了用于本文使用的示例应用程序之一的 Run-as 主体和缺省主体。

清单 13. 显示 Run-as 和缺省主体的安全配置
<security>
    ...
    <default-subject>
        <realm>SampleSecurityRealm</realm>
        <id>dwuser1-subject</id>
    </default-subject>
    <role-mappings>
        <role role-name="ejb3user">
            <run-as-subject>
                <realm>SampleSecurityRealm</realm>
                <id>dwuser3-subject </id>
            </run-as-subject>
            <principal class="o.a.g.s.r.providers.GeronimoGroupPrincipal" name="UserGrp"/>
        </role>
        <role role-name="another">
        ...
        </role>
        ...
    </role-mappings>
</security>

security 元素下的 default-subject 元素配置应用程序使用的缺省主体。default-subject 下的 realm 元素指定安全领域,id 元素指定凭据库中所配置的主体的 ID。请注意,为 default-subject 下的 realm 指定的值与清单 11 中的 realm 元素的 name 属性匹配。另请注意,为 default-subject 下的 id 指定的值与清单 11 中的主体之一的 id 匹配。

role-mappings 下的 role 元素的 run-as-subject 元素配置该角色的 Run-as 主体。清单 13 显示了为 ejb3user 角色配置的 Run-as 主体。请注意,为 run-as-subject 下的 realm 指定的值与清单 11 中的 realm 元素的 name 属性匹配。另请注意,为 run-as-subject 下的 id 指定的值与清单 11 中的主体之一的 id 匹配。


使用 Run-as 的示例 EJB 应用程序

我们已经添加了两个会话 Bean(Simple3ServiceBean2Simple3SeviceBean3),这两个会话 Bean 将调用 Simple3ServiceBean 中对应的方法。我们将 Simple3ServiceBean2 配置为使用 ejb3user 角色运行。Simple3ServiceBean3 未使用 Run-as 角色配置。清单 14 显示了 Simple3ServiceBean2 Bean 类。

清单 14. Simple3ServiceBean2 类
@Stateless
@DeclareRoles(value = {"ejb3user", "ejb3admin"})
@RunAs("ejb3user")
public class Simple3ServiceBean2 implements Simple3Service2 {

	  @EJB
	  private Simple3Service simple; // Simple3Service injected here
	
        @PermitAll
	  public String commonMethod() {
          Object temp;
          try {
              temp = simple.commonMethod();
          } catch(Throwable t) {
              temp = t;
          }
          return logCall("commonMethod") + "::" + temp;
	  }

        @RolesAllowed({"ejb3user"})
	  public String userMethod() {
            ...
	  }

        @RolesAllowed({"ejb3admin"})
	  public String adminMethod() {
            ...
	  }
	  
        @DenyAll
	  public String noaccessMethod() {
            ...
	  }
	  
      private String logCall(String method) {
            ...
	  }
}

在此示例中,我们使用了带 ejb3user 角色的 @RunAs 注释为 Simple3ServiceBean2 配置 Run-as 角色。通过此配置,对 Simple3Service 的方法调用将按照部署计划中指定的使用 Run-as 主体。请注意,ejb3user 角色的 Run-as 主体指定为 dwuser3-subject(请参见清单 13)。


运行使用 Run-as EJB 安全性的示例应用程序

  1. 导航到 Deploy New Portlet。
  2. 部署示例中提供的 simple-ejb-app-w-runas.jar。确保在单击 Install 前选择 Redeploy application 选项。
  3. 导航到 Web App WARs Portlet 并启动 dw/simple-web-app/1.0/war
  4. 访问 http://localhost:8080/simple-web-app。向下滚动到 RunAs 部分,如图 6 中所示。
    图 6. 访问采用 Run-as 的 EJB 的 Web 应用程序主页
    使用 Run-as 的主页的屏幕截图

    请注意,在 RunAs 部分中,Simple3ServiceBean2 中的 commonMethod 使用未经身份验证的用户调用,但 Simple3ServiceBean 中的 commonMethod 按照 Run-as 主体的配置使用 dwuser3 调用。由于 Simple3ServiceBean3 未配置 Run-as 角色,因此 Simple3ServiceBean 中的 commonMethod 使用未经身份验证的用户调用。

  5. 访问 http://localhost:8080/simple-web-app/customer/。以名称 dwuser1 和密码 user1 登录。向下滚动到 RunAs 部分,如图 7 中所示。
    图 7. 访问采用 Run-as 的 EJB 的 Web 应用程序客户页
    使用 Run-as 的客户页的屏幕截图

请注意,在 RunAs 部分,Simple3ServiceBean2 中的 commonMethoduserMethod 使用 dwuser1 调用,即当前登录到 Web 应用程序的用户。不过,在 Simple3ServiceBean 中的 commonMethoduserMethod 按照 Run-as 主体的配置使用 dwuser3 调用。由于 Simple3ServiceBean3 未配置 Run-as 角色,因此 Simple3ServiceBean 中的 commonMethoduserMethod 使用未经身份验证的用户调用。


配置实体 Bean 安全性

可以按照处理会话 Bean 安全性相同的方式使用部署描述符和安全注释配置实体 Bean 安全性。清单 15 显示了带实体 Bean 的示例 EJB 应用程序的组装描述符。

清单 15. 组装描述符
    <assembly-descriptor>
       <security-role>
           <description>Bank Manager</description>
           <role-name>manager</role-name>
       </security-role>
       <method-permission>
           <role-name>manager</role-name>
           <method>
               <ejb-name>MyBank</ejb-name>
               <method-name>create</method-name>
           </method>
       </method-permission>
   </assembly-descriptor>

在此示例中,我们在 MyBank 实体 Bean 的 Home 接口上将 create 方法配置为只能由 manager 角色进行访问。


运行实体 Bean 示例应用程序

示例中提供的 MyBankEJB 示例应用程序包含单个 EJB2.1 实体 Bean,名为 MyBank。我们在 Bean 的 LocalHomeHome 接口中将 create 方法配置为只能由 manager 角色访问。

请遵循以下步骤来运行此应用程序:

  1. 导航到 Deploy New Portlet。
  2. Archive 字段中,浏览到 <SAMPLES_HOME> 并选择 bank-db-pool.rar
  3. 单击 Install,以部署 MyBankEJB 应用程序使用的数据库池。
  4. Archive 字段中,浏览到 <SAMPLES_HOME> 并选择 MyBankEJB.rar
  5. 单击 Install,以部署 MyBankEJB 应用程序。
  6. Archive 字段中,浏览到 <SAMPLES_HOME> 并选择 MyBankWeb.rar
  7. 单击 Install,以部署 MyBankWeb 应用程序。
  8. 访问 http://localhost:8080/MyBankWeb

    请注意,所显示的页面指示在 Create an account 部分出现了异常,因为用户尚未登录。

  9. 现在访问 http://localhost:8080/MyBankWeb/manager

您会注意到所显示的页面现在显示帐户创建成功。之所以成功,是因为页面使用 Run-as 角色配置为使用用户 ID dwadmin 访问 MyBank Bean,此用户 ID 在 MyBankEJB 部署计划中配置为映射到 manager 角色。


配置消息驱动的 Bean 安全性

与会话 Bean 和实体 Bean 不同,消息驱动的 Bean 不会由其他 Bean 或客户端应用程序调用。它们在消息到达 JMS 侦听器监视的输入目的地时由此侦听器调用。消息驱动的 Bean 可以调用其他会话 Bean 和实体 Bean。要从消息驱动的 Bean 调用安全 EJB,需要为消息驱动的 Bean 配置 Run-as 角色。对于 EJB2.1 消息驱动的 Bean,可以在部署描述符中使用 security-identity 元素配置 Run-as 角色。对于 EJB3 消息驱动的 Bean,可以使用 @RunAs 注释配置 Run-as 角色。


运行消息驱动的 Bean 示例应用程序

为了演示 EJB3 消息驱动的 Bean 的 Run-as 安全性,我们在示例中提供了一个应用程序 (jms-mdb-sample-ear-2.1.0.1.ear)。此应用程序包括一个名为 OrderRecvMDB 的消息驱动的 Bean 和一个名为 Simple30ServiceBean 的无状态会话 Bean。我们将会话 Bean 的 userMethod 配置为可由 ejb3user 角色访问,OrderRecvMDB Bean 配置了 Run-as 角色 ejb3user。在部署计划中,安全性配置为 ejb3user 角色指定了 Run-as 主体 dwuser1-subject。清单 16 显示了 OrderRecvMDB Bean 类:

清单 16. OrderRecvMDB Bean 类
import javax.annotation.security.RunAs;
...

//
// MessageDrivenBean that listens to items on the
// 'OrderQueue' queue and processes them accordingly.
//
@MessageDriven(activationConfig = {
       @ActivationConfigProperty(propertyName="destinationType", 
           propertyValue="javax.jms.Queue"),
       @ActivationConfigProperty(propertyName="destination", 
           propertyValue="OrderQueue")
   })
@RunAs("ejb3user")               
public class OrderRecvMDB implements MessageListener {
	
	@EJB
	private Simple30Service simple; // Session bean is injected

	/*
     * Process a message.
     * 
     * @param message The message to process. 
     */
    public void onMessage(Message message) {
        TextMessage textMessage = (TextMessage) message;
        try {
            System.out.println("Order Received \n"+ textMessage.getText());
            simple.userMethod();
        } catch ( JMSException e ) {
            e.printStackTrace();
        }
    }
}

请遵循以下步骤来运行此应用程序:

  1. 导航到 Deploy New Portlet。
  2. Archive 字段中,浏览到 <SAMPLES_HOME> 并选择 jms-mdb-sample-ear-2.1.0.1.ear
  3. 单击 Install,以部署示例应用程序。
  4. 访问 http://localhost:8080/order
  5. 输入字段值,并单击 Order

请注意,服务器控制台窗口中显示接收到的顺序以及关于用于调用 Simple30ServiceBean 中的 userMethod 的安全标识 dwuser1 的详细信息。


结束语

我们创建了 EJB2.1 和 EJB3 会话 Bean、EJB2.1 实体 Bean,并使用部署描述符和安全注释为 Bean 方法配置了访问权限。我们还为会话 Bean 和消息驱动的 Bean 配置了 Run-as 角色,并创建了所需的构件(如凭据库)来部署使用此安全配置的 EJB 应用程序。我们还通过使用示例 Web 应用程序调用 EJB 来演示了 EJB 安全性。


致谢

感谢 Phani Madgula 对本文进行了审阅。


下载

描述名字大小
示例代码samples.zip1.6MB

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere, Open source
ArticleID=387767
ArticleTitle=使用 WebSphere Application Server Community Edition 配置 EJB 应用程序安全性
publish-date=05132009