在 WebSphere Application Server Community Edition V2.0 中配置 Web 应用程序安全性

为在 IBM® WebSphere® Application Server Community Edition V2.0(一种免费使用的 Java™ EE 5 认证的、基于 Apache Geronimo 2.0.1 的应用服务器)中部署的 Web 应用程序配置安全性。本文将向您介绍 Community Edition 所提供的各种安全领域,以便您可以为自己的 Web 应用程序确定并实现最好的且最合适的安全级别。

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

Author photoVamsavardhana Reddy Chillakuru是位于印度班加罗尔的 IBM India Software Labs 的一名顾问软件工程师。他在 Apache Geronimo 和 WebSphere Application Server Community Edition 的支持团队工作。您可以通过 vamsic007@in.ibm.com 与 Vamsi 联系。


developerWorks 投稿作者

Manu T. George (mageorge@in.ibm.com), 软件工程师, IBM

Manu T. George 是位于印度班加罗尔市的 IBM Software Labs 的一位软件工程师。他是 IBM WebSphere Application Server Community Edition Level 3 Support Team 中的一员。George 曾在特里凡得琅市的工程技术学院获得应用电子专业学士学位。



2007 年 10 月 18 日

引言

IBM WebSphere Application Server Community Edition V2.0(以下称为 Community Edition)是一个免费使用的 Java Platform, Enterprise Edition 5.0 (Java EE 5) 认证的、基于 Apache Geronimo 2.0.1 的应用服务器。Community Edition 使用 Java 身份验证和授权服务 (JAAS) 登录模块在 Web 应用程序中对用户进行身份验证,使用 Java Authorization Contract for Containers (JACC) 对用户进行授权

Community Edition 提供了各种登录模块的实现,这些登录模块允许您在各种数据存储中,如简单文本文件、数据库(比如 IBM DB2® 或者 Cloudscape™)或者 LDAP 服务器(比如 Apache Directory Server 或者 IBM Tivoli® Directory Server),存储身份验证凭据。要访问这些数据存储,您需要创建并适当地配置安全领域。本文将向您展示在创建 Community Edition 提供的安全领域时所需的内容,以及如何使用它们来配置 Web 应用程序安全性。您还将了解如何启用审核、重复登录失败尝试的锁定,以及如何为以后的应用程序存储用户凭据

本文需要 WebSphere Application Server Community Edition V2.0 或更高的版本。基于各种登录模块实现(使用数字证书,或由用户实现的登录模块实现)的安全领域超出了本文的范围。


JAAS 和 JACC 是什么?

Java 身份验证和授权服务(Java Authentication and Authorization Service,JAAS)实现了 Java 版本的标准插入式验证模块(Pluggable Authentication Module,PAM)框架。JAAS 通过在应用程序和基础身份验证机制之间设置一个抽象层来简化 Java 安全开发,从而使得应用程序能够独立于身份验证机制。可以将新的或者经过更新的身份验证机制插入其中,而无需对应用程序做任何修改。应用程序通过实例化 LoginContext 对象来启动身份验证,而这个 LoginContext 对象可以引用一个配置,该配置可以在执行身份验证过程中确定是否使用身份验证机制或者 LoginModules。如果身份验证成功,那么 LoginModule 将使用相关的主体和凭据更新该主题。

Java Authorization Contract for Containers (JACC) 规范 (JSR-115) 定义了一些新的 java.security.Permission 类,以满足 Java EE 5 授权模型。JACC 允许根据这些 permission 类进行授权决策。

有关 JAAS 和 JACC 的更详细的信息,请参见参考资料


管理控制台

标准的管理控制台提供了一种便利的、用户友好的方式,以管理 Community Edition 中的许多组件以及多方面内容。其中提供了几个 Portlet,以执行各种管理任务,如Security Realms PortletApplications PortletDB Manager PortletDatabase Pools Portlet。在本文中,将使用这些 Portlet。管理控制台还提供了一些使得管理员能够监视或者观察服务器状态的 Portlet,如 InformationJVMDB Info Portlet。启动 Community Edition 后,就可以通过 http://localhost:8080/console 访问管理控制台。缺省的登录名为 system,密码为 manager

Security Realms Portlet

Database Pools Portlet 允许您添加新的安全领域,并编辑现有的安全领域。该 Portlet 还提供了 usage 链接,它提供了有关如何配置应用程序,以便根据安全领域来进行身份验证的信息。本文使用 Security Realms Portlet 来添加下面这些新的领域:

要启动 Security Realms Portlet,请在 Console Navigation 窗格中的 Security 标题下选择 Security Realms 链接(图 1)。

图 1. Console Navigation
图 1. Console Navigation

请注意 Security Realms Portlet 中的三个链接:

  • edit 操作链接用于编辑现有的安全领域。
  • usage 操作链接用于显示在您的 Java EE 应用程序中如何使用这个安全领域。
  • Add new security realm 用于创建一个新的安全领域。

在接下来的部分中,您将使用这些链接来创建和编辑安全领域,并观察您创建的安全领域的使用情况。

Applications Portlet

在 Console Navigation 窗格中的 Applications 标题下方列出了几个 Applications Portlet(图 1)。这些 Portlet 允许您部署新的 Web、EJB 和 Java EE 应用程序,以及应用程序客户端、Java EE 连接器等等。您还可以启动、停止和卸载现有的应用程序、连接器等等。

本文使用 Deploy New Portlet 部署所包括的示例 Web 应用程序。单击 Console Navigation 窗格中的 Applications 标题下方的 Deploy New,以启动该 Portlet(图 2)。

图 2. Deploy New Portlet
图 2. Deploy New Portlet

Web App WARs Portlet(通过在 Console Navigation 窗格中单击 Web App WARs 来启动)列出了在 Community Edition 中部署的所有 WAR。使用这个 Portlet(图 3)以启动、停止、重新启动或者卸载任何这些应用程序。Web App WARs Portlet 还提供一个包含上下文根的 URL,以访问正在运行的 Web 应用程序。

图 3. Web App WARs Portlet
图 3. Web App WARs Portlet

DB Manager Portlet

DB Manager Portlet(图 4)允许您访问 Community Edition 中嵌入的 Derby 数据库服务器。通过单击 Console Navigation 窗格(图 1)中的 Embedded DB 标题下方的 DB Manager 链接来启动并加载 DB Manager Portlet。

图 4. DB Manager Portlet
图 4. DB Manager Portlet

使用这个 DB Manager,您可以完成以下任务:

  • 创建新的嵌入式 Derby 数据库。
  • 删除现有的嵌入式 Derby 数据库。
  • 针对现有的 Derby 数据库运行 SQL 查询。

当您在本文的稍后部分中创建数据库域的时候,您将执行其中的每一项任务。

Database Pools Portlet

通过在 Console Navigation 窗格中的 Services 标题下方单击 Database Pools 来启动数据库池 Portlet(图 5)。

图 5. 数据库池 Portlet
图 5. 数据库 Portlet

图 5 显示了两个数据库池,每个数据库池都提供了到 editdelete 的链接,并可以获得该池的使用情况。该 Portlet 还提供了使用 Geronimo 数据库池向导创建新的数据库池的链接,以及从 Weblogic 8.1 和 JBoss 4 导入现有数据库池的链接。在本文稍后的部分中,您将了解如何使用 Geronimo 数据库池向导来创建数据库池。


安全领域

安全领域,顾名思义,对应于身份验证信息所属的域或者数据范围;即身份验证信息所适用的领域。安全领域用于确保对资源的安全访问。例如,名为 geronimo-admin 的属性文件域可以确保对 Community Edition 管理控制台的安全访问。在 Community Edition 中,安全领域是一个或者多个登录模块的组合。

接下来的部分将讨论可以使用 Community Edition 提供的 PropertiesFileLoginModule、SQLLoginModule 和 LDAPLoginModule 实现来创建的一些安全领域:

属性文件域

我们通常将基于 PropertiesFileLoginModule 的安全领域称为属性文件域。这是 Community Edition 所提供的最简单的安全领域。可以使用文本编辑器来管理身份验证信息。一个属性文件域由两个属性文件组成(显而易见),它将使用基础的数据存储。要创建并使用属性文件域,需要完成下面这些任务:

  1. 创建属性文件

    一个属性文件域使用两个属性文件:一个用于存储用户身份验证数据,称为用户属性文件,另一个用于存储数据分组信息,称为组属性文件。清单 1 和清单 2 分别显示了本文中所使用的 dw_users.properties 文件和 dw_groups.properties 文件的内容。请注意,在组属性文件中使用的用户名还必须存在于用户属性文件中。另外,用户既可以不属于任何组又可以属于多个组。

    清单 1. dw_users.properties
    #This is a comment line
    #Entries are in the format username=password and only one entry per line
    #We have five users namely dwadmin, dwuser1, dwuser2, root and john
    dwadmin=admin
    dwuser1=user1
    dwuser2-user2
    root=root
    john=brad1983
    清单 2. dw_groups.properties
    #This is a comment line
    #Entries are in the format groupname=<comma separated usernames in the group>
    #We have three groups namely Admin, User and Guest
    Admin=dwadmin,root
    User=dwuser1,dwuser2,root
    Guest=john

    要在安全领域中使用这些文件,应该将它们放置于 <WASCE_HOME> 下的目录中,通常我们将其放置于 <WASCE_HOME>/var/security 下。(其中,<WASCE_HOME> 指的是安装 Community Edition 的目录。)

    此时,将本文所包括的 wascesecurity_samples.zip 下载文件的内容解压缩到您所选择的目录中。(这里将您所选择的目录称为 <SAMPLES_HOME>。)将 dw_users.properties 和 dw_groups.properties 文件从 <SAMPLES_HOME> 目录复制到 <WASCE_HOME>/var/security。

  2. 创建安全领域

    要创建属性文件域,需要完成以下工作:

    1. 从 Console Navigation 窗格启动 Security Realms Portlet,然后在 Security Realms 窗格中选择 Add new security realm(图 1)。步骤 1 将显示用于创建安全领域的面板(图 6)。

    2. 输入或者选择下列这些值:

      • Name of Security Realm: SampleSecurityRealm
      • Realm Type:Properties File Realm

      单击 Next

      图 6. 步骤 1 - 选择名称和类型
      图 6. 步骤 1 - 选择名称和类型
    3. 在步骤 2 面板(图 7)中,输入以下值:

      • Users File URI: var/security/dw_users.properties
      • Groups File URI: var/security/dw_groups.properties

      这些 URI 的值应该是相对于 <WASCE_HOME> 目录的位置。其余的字段保留为空白(稍候将讨论它们)。单击 Next

      图 7. 步骤 2 - 配置登录模块
      图 7. 步骤 2 - 配置登录模块
    4. 步骤 3 面板(图 8)显示了四个复选框选项:

      • Enable Auditing:将对指定文件的每次尝试都进行日志记录。
      • Enable Lockout:如果在某段时间内某个用户出现了一定次数的失败登录,那么将对该用户的帐号锁定指定长度的一段时间(其中所有的值都是可以自定义的)。
      • Store Password:当启用该选项时,安全领域将在这个主题中的私有凭据中存储对用户进行身份验证的用户名和密码,从而在完成登录过程之后,允许对该密码进行访问。
      • Named Credential:当启用该选项时,安全领域将在主题中指定凭据名下的私有凭据中存储对用户进行身份验证的用户名和密码。

      接下来,保留所有的选项为未选中状态。在本文的稍后部分中将对它们进行讨论。

      图 8. 步骤 3 - 高级配置
      图 8. 步骤 3 - 高级配置
    5. 选择 Test a Login。这时将显示步骤 4 面板(图 9)。

      图 9. 步骤 4 - 测试登录
      图 9. 步骤 4 - 测试登录
    6. 输入一个有效的用户名和密码(对于这个示例,分别使用 johnbrad1983)并单击 Next。这时将显示登录成功页面(图 10)。

      图 10. 步骤 5 - 登录结果
      图 10. 步骤 5 - 登录结果
    7. 单击 Show Plan,以观察将用于部署这个安全领域的部署计划。清单 3 中显示了这个部署计划。

      清单 3. 安全领域部署计划
      <module xmlns="http://geronimo.apache.org/xml/ns/deployment-1.2">
         <environment>
            <moduleId>
               <groupId>console.realm</groupId>
               <artifactId>SampleSecurityRealm</artifactId>
               <version>1.0</version>
               <type>car</type>
            </moduleId>
            <dependencies>
               <dependency>
                  <groupId>org.apache.geronimo.configs</groupId>
                  <artifactId>j2ee-security</artifactId>
                  <type>car</type>
               </dependency>
            </dependencies>
         </environment>
         <gbean name="SampleSecurityRealm"
            class="org.apache.geronimo.security.realm.GenericSecurityRealm"
            xsi:type="dep:gbeanType"
            xmlns:dep="http://geronimo.apache.org/xml/ns/deployment-1.2"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <attribute name="realmName">SampleSecurityRealm</attribute>
            <reference name="ServerInfo">
               <name>ServerInfo</name>
            </reference>
            <xml-reference name="LoginModuleConfiguration">
               <log:login-config
                  xmlns:log="http://geronimo.apache.org/xml/ns/loginconfig-2.0">
                     <log:login-module control-flag="REQUIRED"
                        wrap-principals="false">
                        <log:login-domain-name>
                           SampleSecurityRealm
                        </log:login-domain-name>
                        <log:login-module-class>
         org.apache.geronimo.security.realm.providers.PropertiesFileLoginModule
                        </log:login-module-class>
                        <log:option name="groupsURI">
                           var/security/dw_groups.properties
                        </log:option>
                        <log:option name="usersURI">
                           var/security/dw_users.properties
                        </log:option>
                     </log:login-module>
               </log:login-config>
            </xml-reference>
         </gbean>
      </module>

      在这个部署计划中要遵守如下的几个重要事项:

      • moduleId:为这个配置分配一个 ID“console.realm/SampleSecurityRealm/1.0/car”,对应于这个安全领域将要运行的位置。
      • 这个配置包含一个名为 SampleSecurityRealm 的 GBean,并且 realmName 属性与 gbean 标记中的 name 属性相匹配。
      • 登录模块类是 org.apache.geronimo.security.realm.providers.PropertiesFileLoginModule。
      • 选项 groupsURI 和 usersURI 是特定于 PropertiesFileLoginModule 的。
    8. 此时,您可以选择将该计划保存到磁盘,并且使用 Deploy New Portlet 来部署该计划。您将无需指定存档文件以部署这个计划(图 2)。只需单击 Deploy Realm 按钮以完成该安全领域的创建。图 11 显示了该情况。

      图 11. Security Realms Portlet
      图 11. Security Realms Portlet

    您已经成功地创建了一个名为 SampleSecurityRealm 的属性文件域。当根据 SampleSecurityRealm 对用户进行身份验证的时候,该主题将包含一个 GeronimoUserPrincipal(使用经过身份验证的用户名作为在该主体中的名称)、零个或者多个 GeronimoGroupPrincipals(使用经过身份验证的用户名所属的组作为在该主体中的名称)。这个安全领域使用 ID“console.realm/SampleSecurityRealm/1.0/car”在该配置中运行。

  3. 配置 Web 应用程序安全性

    接下来,您将看到如何使用上面创建的安全领域在简单的 Web 应用程序中配置安全性。通常,可以通过在 Web 应用程序的 Java EE 特定的部署描述符 web.xml 文件中创建安全角色和安全约束来配置它的安全性。然后将这些安全角色映射到服务器特定的部署计划(在应用程序部署时使用的部署计划)中的主体(在 Community Edition 的情况下是 geronimo-web.xml)。这里所使用的示例应用程序由三个 JSP 组成:一个不受访问限制,一个可以通过 admin 角色访问,另一个可以通过 user 角色访问。该应用程序的 web.xml 部署描述符如清单 4 中所示。

    清单 4. 示例应用程序的 web.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app id="SimpleWebApp" version="2.5" ... >
        <display-name>SimpleWebApp</display-name>
        <login-config>
            <auth-method>BASIC</auth-method>
            <!-- For 'BASIC', realm-name will be shown in the prompt -->
            <realm-name>Sample Security Realm</realm-name>
        </login-config>
    
        <!-- Security roles used in the application -->
        <security-role><role-name>admin</role-name></security-role>
        <security-role><role-name>user</role-name></security-role>
    
        <!-- Configure authorization for Admin pages -->    
        <security-constraint>
            <web-resource-collection>
                <web-resource-name>Admin</web-resource-name>
                <url-pattern>/admin/*</url-pattern>
                <http-method>GET</http-method>
                <http-method>POST</http-method>
            </web-resource-collection>
            <auth-constraint>
                <role-name>admin</role-name>
            </auth-constraint>
        </security-constraint>
        <!-- Configure authorization for User pages -->
        <security-constraint>
            <web-resource-collection>
                <web-resource-name>User</web-resource-name>
                <url-pattern>/user/*</url-pattern>
                <http-method>GET</http-method>
                <http-method>POST</http-method>
            </web-resource-collection>
            <auth-constraint>
                <role-name>user</role-name>
            </auth-constraint>
        </security-constraint>
    </web-app>

    在这个 web.xml 文件中:

    • <login-config> 标记指定了对用户进行身份验证的方法,以及所需的任何附加配置。在这个示例中,auth-method 的值 BASIC 意味着将使用由浏览器显示的一个对话框来进行登录。对于这个 auth-method,您可以有选择地指定一个用于 realm-name 的值,它将在用于登录的对话框中显示。
    • <security-role> 标记声明了您希望在这个应用程序中使用的安全角色。在一个应用程序中可以没有安全角色,也可以有多个安全角色。在这个示例中,我们使用了两个安全角色:admin 和 user。
    • <security-constraint> 标记声明了资源的授权约束,而 <url-pattern> 标记则指定了该约束所适用的 URL 模式。在这个示例中,/admin/* 意味着所有以“/admin/”开头的 URL 都将受制于这个安全约束。假设该应用程序可以通过 http://localhost:8080/simplewebapp 访问,那么其中一些 URL 可能是 http://localhost:8080/simplewebapp/admin/admin.jsp、http://localhost:8080/simplewebapp/admin/another.jsp 等等。<auth-constraint> 标记指定了可以访问这个 URL 模式的安全角色。

    login-config(在应用程序部署描述符内)中的 <auth-method> 标记允许您指定 Web 应用程序所使用的身份验证方法。其有效值可以是 BASIC、DIGEST、FORM 和 CLIENT-CERT:

    • 当指定 BASIC 或者 DIGEST 的时候,将使用一个浏览器特定的对话框进行登录。在本文提供的某些示例应用程序中,使用了 BASIC auth-method。

    • 对于 FORM auth-method,将使用一个登录页面(使用 form-login-config 进行配置)进行登录。在清单 5 中显示了为进行 FORM 身份验证而配置的 SimpleWebApp-LDAP 示例应用程序中的 web.xml 文件的摘录。在清单 6 中显示了应该如何将该表单编码为 HTML 页面的一个示例。

      清单 5. login-config
      <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>
      清单 6. 登录表单
      <form method="POST" action="j_security_check">
          <input type="text" name="j_username">
          <input type="text" name="j_password"> 
      </form>

      登录表单的操作必须始终是 j_security_check。应该使用 j_username 输入字段来获取用户名,同时使用 j_password 输入字段来获取用户的密码。

    • 如果应用程序使用数字证书来对客户进行身份验证,那么请使用 CLIENT-CERT。

    一旦在 web.xml 中定义了安全角色和约束,那么将在应用程序部署计划 geronimo-web.xml 中,把安全角色映射为主体。在清单 7 中显示了这个示例应用程序的部署计划。

    清单 7. 示例应用程序的 geronimo-web.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://geronimo.apache.org/xml/ns/j2ee/web-2.0">
        <dep:environment xmlns:dep="http://geronimo.apache.org/xml/ns/deployment-1.2">
            <dep:moduleId>
                <dep:groupId>dw</dep:groupId>
                <dep:artifactId>SimpleWebApp</dep:artifactId>
                <dep:version>1.0</dep:version>
                <dep:type>war</dep:type>
            </dep:moduleId>
            <dep:dependencies>
                <dep:dependency>
                    <dep:groupId>console.realm</dep:groupId>
                    <dep:artifactId>SampleSecurityRealm</dep:artifactId>
                    <dep:version>1.0</dep:version>
                    <dep:type>car</dep:type>
                </dep:dependency>
            </dep:dependencies>
        </dep:environment>
        <context-root>simplewebapp</context-root>
        <security-realm-name>SampleSecurityRealm</security-realm-name>
        <security>
            <role-mappings>
                <role role-name="admin">
                    <principal name="Admin"
    	class="org.apache.geronimo.security.realm.providers.GeronimoGroupPrincipal"/>
                </role>
                <role role-name="user">
                    <principal name="User"
    	class="org.apache.geronimo.security.realm.providers.GeronimoGroupPrincipal"/>
                    <principal name="john"
    	class="org.apache.geronimo.security.realm.providers.GeronimoUserPrincipal"/>
                </role>
            </role-mappings>
        </security>
    </web-app>

    在 geronimo-web.xml 文件中:

    • environment 部分中的 <moduleId> 标记允许您为您的应用程序指定一个配置 ID。如果省略这个内容的话,那么 Community Edition 将应用它自己的规则为您的应用程序分配一个配置 ID。在这个示例中,其值为:dw/SimpleWebApp/1.0/war。
    • environment 部分中的 <dependencies> 标记允许您指定您的应用程序对运行于该服务器中的其他配置的依赖关系。在这个示例中,在正在运行属性文件域(先前创建的)的配置上指定了一项依赖关系:console.realm/SampleSecurityRealm/1.0/car。
    • <context-root> 标记指定了用户可以使用您的应用程序的上下文根。在这个示例中,其值为“simplewebapp”,这意味着,可以通过下面的 URL 来访问该应用程序:http://localhost:8080/simplewebapp。
    • <security-realm-name> 标记指定了这个应用程序进行身份验证的安全领域名称。前面曾说过,SampleSecurityRealm 是用于您先前创建的属性文件域的名称。
    • role-mappings 中的 <role> 标记允许您配置应该映射到每个角色的主体。role-name 属性用于指定要进行映射的 role 标记中的主体元素的角色。这里,将 admin 角色映射到名为“Admin”的 GeronimoGroupPrincipal,并且将“user”角色映射到两个主体:一个名为“User”的 GeronimoGroupPrincipal 和一个名为“john”的 GeronimoUserPrincipal。前面曾说过,当用户登录到 SampleSecurityRealm 的时候,使用一个带有用户名的 GeronimoUserPrincipal,以及零个或者多个带有该用户所属的所有组的名称的 GeronimoGroupPrincipal 来填充该主题。

    在某个用户登录之后,就可以使用 request.getUserPrincipal() 方法在 Servlet 或者 JSP 中获得经过身份验证的用户主体。这个用户是否采用了特定的角色,可以使用 request.isUserInRole() 方法来确定。清单 8 中显示了 admin.jsp 的摘录。

    清单 8. admin.jsp
    <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
        pageEncoding="ISO-8859-1"%>
    <html>
    ...
    <br>Welcome <b><%=request.getUserPrincipal().getName() %></b> !!
    <br>
    <br>You are <%=request.isUserInRole("admin") ? 
    	"in" : "not in"%> 'admin' role.
    <br>You are <%=request.isUserInRole("user") ? 
    	"in" : "not in"%> 'user' role.
    ...
    </html>
  4. 安装示例应用程序

    现在,您将使用 Deploy New Portlet 来部署示例应用程序。本文所提供的下载文件中包含了 WAR 文件 SimpleWebApp.war。因为部署计划 (geronimo-web.xml) 打包于 WAR 文件的 WebContent/WEB-INF 目录中,所以您并不需要指定一个计划文件以安装该应用程序:

    1. 启动 Deploy New Portlet(图 2)。
    2. 导航到存档文件位置 <SAMPLES_HOME>\SimpleWebApp.war,并单击 Install,以安装该应用程序。
    3. 验证在 Web App WARs Portlet 中列出了已安装的应用程序(图 3)。
  5. 测试应用程序

    您所部署的示例应用程序可以通过 http://localhost:8080/simplewebapp 来访问。您可以使用下面的几个场景来测试该应用程序:

    场景 1:

    1. 打开一个新的浏览器窗口,并访问 http://localhost:8080/simplewebapp。
    2. 单击该链接以访问 Admin 页面。这时将显示一个对话框,以提供登录信息。请注意,这个对话框显示了在 web.xml 中所配置的 real-name“Sample Security Realm”(图 12)。
    3. 对于用户名和密码,分别输入 dwadminadmin。单击 OK
    4. Admin 页面应该是可以访问的。单击该链接以访问 User 页面。您应该接收到一条错误信息“HTTP Status 403”。
    图 12. Web 应用程序登录
    图 12. Web 应用程序登录

    刚刚发生的是:

    • 以 dwadmin 身份的登录将向该主题添加两个主体:GeronimoUserPrincipal(”dwadmin”) 和 GeronimoGroupPrincipal(“Admin”)。
    • 因为在 admin 角色的 geronimo-web.xml 中的 role-mapping 列出了 GeronimoGroupPrincipal("Admin"),所以 dwadmin 可以通过 http://localhost:8080/simplewebapp/admin/admin.jsp 访问 Admin 页面。
    • 因为在该主题中,两个主体中的任何一个都没有出现在 user 角色的 role-mapping 中,所以禁止 dwadmin 通过 http://localhost:8080/simplewebapp/user/user.jsp 访问 User 页面。

    场景 2:

    1. 打开一个新的浏览器窗口,并访问 http://localhost:8080/simplewebapp。
    2. 以用户 dwuser1 的身份、并使用密码 user1 登录。您将可以访问 User 页面,但是如果您访问 Admin 页面,那么将得到一条错误信息“HTTP Status 403”。

    刚刚发生的是:

    • 以 dwuser1 身份的登录将向该主题添加两个主体:GeronimoUserPrincipal("dwuser1") 和 GeronimoGroupPrincipal("User")。
    • 因为 user 角色的 role-mapping 列出了 GeronimoGroupPrincipal("User"),所以 dwuser1 可以访问 User 页面。
    • 因为在该主题中,两个主体中的任何一个都没有出现在 admin 角色的 role-mapping 中,所以禁止 dwuser1 访问 Admin 页面。

    场景 3:

    1. 打开一个新的浏览器窗口,并访问 http://localhost:8080/simplewebapp。
    2. 以用户 root 的身份、并使用密码 root 登录。您将可以访问 User 和 Admin 两个页面。

    刚刚发生的是:

    • 以 root 身份的登录将向该主题添加三个主体:GeronimoUserPrincipal("root")、GeronimoGroupPrincipal("Admin") 和 GeronimoGroupPrincipal("User")。
    • 因为 admin 角色的 role-mapping 列出了 GeronimoGroupPrincipal("Admin"),所以用户 root 可以访问 Admin 页面。
    • 因为 user 角色的 role-mapping 列出了 GeronimoGroupPrincipal("User"),所以用户 root 可以访问 User 页面。

    场景 4:

    1. 打开一个新的浏览器窗口,并访问 http://localhost:8080/simplewebapp。
    2. 以用户 john 的身份、并使用密码 brad1983 登录。您将可以访问 User 页面,但是如果您访问 Admin 页面,那么将得到一条错误信息“HTTP Status 403。”

    刚刚发生的是:

    • 以 john 身份的登录将向该主题添加两个主体:GeronimoUserPrincipal("john") 和 GeronimoGroupPrincipal("Guest")。
    • 因为 user 角色的 role-mapping 列出了 GeronimoUserPrincipal("john"),所以 john 可以访问 User 页面。
    • 因为在该主题中,两个主体中的任何一个都没有出现在 admin 角色的 role-mapping 中,所以禁止 john 访问 Admin 页面。

此时,您已经创建了一个属性文件域,并将 Web 应用程序配置为根据这个安全领域进行身份验证,还验证了该应用程序中对资源的授权。

数据库 (SQL) 域

通常,将基于 SQLLoginModule 的安全领域称为数据库域,并且可以将其配置为使用存储于数据库中的凭据对用户进行身份验证。这是一种比属性文件域更加可靠的安全领域,但是为了实现该域,您需要创建相应的表,并在数据库中输入数据。在这个示例中,您将使用 Community Edition 提供的嵌入式 Cloudscape 数据库。您将使用 DB Manager Portlet 在嵌入式 Cloudscape 实例中创建一个新的数据库,并连接到该数据库,创建表以及插入数据。要连接到该数据库,可以将 SQLLoginModule 配置为直接使用 JDBC,或者使用 Community Edition 中的连接池。在这个示例中,您将创建一个连接池,并将 SQLLoginModule 配置为使用该连接池来进行对数据库的访问。为了创建并使用数据库域,您需要完成如下工作:

  1. 创建一个数据库

    创建数据库域的第一步是,创建一个数据库实例(如果您还没有任何数据库实例的话)。在这个示例中,您将使用 Community Edition 中嵌入的 Cloudscape 数据库,因此您将首先创建一个嵌入式的 Cloudscape 数据库实例:

    1. 打开 DB Manager Portlet(图 4)。
    2. 对于 Create DB,输入 SAMPLEDB
    3. 单击 Create 按钮,将创建该数据库。
  2. 创建表

    一个数据库域需要在安全领域中配置两条 SQL 语句:

    • User SELECT SQL
      顾名思义,这是一条用以加载用户名和密码信息的 SQL 语句。它应该返回两个列,第一列中包含用户名,而第二列中包含密码。

    • Group SELECT SQL
      这是一条用于为用户加载组信息的 SQL 语句。它应该返回两个列,第一列中包含用户名,而第二列中包含组名。

    这两条语句都可以使用带“?”的 PreparedStatement 语法作为一个参数(请参见参考资料),在这种情况下,将为每个参数设置用户名。

    现在,您将创建两个表,用于存储用户信息和组信息,这两个表将分别命名为“users”和“groups”。本文所提供的下载文件中的 db-realm-tables.sql 文件提供了创建这些表的 SQL 语句,如清单 9 所示。

    清单 9. db-realm-tables.sql
    create table users (username varchar (15), password varchar (64));
    create table groups (groupname varchar (15), username varchar (15));

    要创建这些表,需要完成以下工作:

    1. 通过在 Console Navigation 窗格中单击 DB Manager(图 1)启动 DB Manager Portlet。
    2. 在 Use DB 下拉列表中,选择 SAMPLEDB 作为您将要运行 SQL 的数据库(图 4)。
    3. 复制清单 9 中的命令,并将其粘贴到 SQL Command/s 字段中,然后单击 Run SQL。执行这些 SQL 命令,这将创建所需的表。
  3. 创建数据库池

    另一个重要的步骤是创建数据库池,数据库域中的 SQLLoginModule 将使用这个数据库池来访问包含身份验证信息的表。该数据库池将保持到 SAMPLEDB 数据库实例的一个连接池。要创建数据库池,需要完成以下工作:

    1. 选择 Database Pools Portlet(图 5)。
    2. 在“Create a new database pool”的部分中,选择 Using the Geronimo database pool wizard。图 13 显示了该情况。
      图 13. 步骤 1 选择名称和数据库
      图 13. 步骤 1 选择名称和数据库
    3. 对于 Name of Database Pool,输入 DBRealmPool,对于 Database Type,选择 Derby network。单击 Next
    4. 在接下来的对话框(图 14)中,输入或者选择下面的值:
      • Driver JAR:org.apache.derby/derbyclient/10.2.2.0/jar
      • Database Name: SAMPLEDB
      • User Name: APP
      • Password 和 Confirm Password: password
      • Port Number: 1527
      • Pool Min Size: 5
      • Pool Max Size: 20
      图 14. 编辑一个新的数据库池
      图 14. 编辑一个新的数据库池
  4. 创建并测试安全领域

此时,您已经了解了数据库域的先决条件,并创建了数据库域所需的各种构件。在本文后面的部分中,将会讨论创建安全领域,并使用示例应用程序结合某些高级功能对它进行测试的步骤。

LDAP 域

通常将基于 LDAPLoginModule 的安全领域称为 LDAP 域。LDAP 域根据存储于 LDAP 服务器中的凭据对用户进行身份验证。可以将这种安全领域配置为使用运行于 Community Edition 实例内部或者外部的 LDAP 服务器。就本文而言,将使用作为插件安装于 Community Edition 中的 Apache Directory Server 1.5.0。要创建并使用 LDAP 域,需要完成以下工作:

  1. 安装 Apache Directory Server 插件

    可以使用管理控制台中的 Plugins Portlet 将插件存储库中提供的插件安装到 Community Edition 中。截至本文撰写之时,还没有发布任何 Apache Directory Server 插件,也没有插件存储库提供任何 Apache Directory Server 插件。出于演示的目的,在本文附带提供的下载资料中包括了一个目录服务器插件文件,您可以使用命令行部署工具来安装该插件。要安装目录服务器插件,需要完成以下工作:

    1. 将 <SAMPLES_HOME>\directory-plugin-extras.zip 解压缩到 <WASCE_HOME>。
    2. 当 Community Edition 服务器运行的时候,在命令窗口中使用这个命令部署插件 CAR 文件:

      <WASCE_HOME>\bin\deploy install-plugin <SAMPLES_HOME>\directory-2.0-SNAPSHOT.car

    3. 当提示输入用户名和密码的时候,分别输入 systemmanager。(这与访问管理控制台的用户名和密码是相同的。)

    这个操作可能需要花费一些时间,因为安装程序将从远程 Maven 存储库下载 Apache Directory Server JAR 和其他存在依赖关系的内容。在安装并启动了该插件之后,将在安装程序窗口中显示一条消息“Started org.apache.geronimo.plugin/directory/2.0-SNAPSHOT/car”。

    您所安装的 Apache Directory Server 是一个 LDAP v3 服务器。它运行于端口 10389,使用“简单的”身份验证,并可以使用用户 DN“uid=admin,ou=system”和密码“secret”对它进行访问。访问目录服务器将需要这些属性,且这些属性将在稍后配置 LDAP 域时起到重要的作用。请参见目录服务器所使用的配置属性的 <WASCE_HOME>/var/directory/server.xml。(管理 Apache Directory Server 超出了本文的范围。)

  2. 向目录服务器中添加数据

    在 LDAP 域中使用目录服务器之前,需要将身份验证的数据添加到该目录服务器。就本文而言,您将使用 JXplorer(一种开放源代码 LDAP 浏览器,请参见参考资料),将某些存储于 LDIF 文件中的用户凭据信息添加到目录服务器中。在本文中,我们使用的是 JXplorer v3.2。下载资料中提供了文件 dw-ldap-realm.ldif,其中包含五组用户/密码对:

    • dwadmin/adminldap
    • dwuser1/user1
    • dwuser2/user2
    • root/root
    • john/brad1983

    和三个组:

    • 包含成员 dwadmin、root 的 Admin 组
    • 包含成员 dwuser1、dwuser2、root 的 User 组
    • 包含成员 john 的 Guest 组

    在安装了 Jxplorer 之后,请执行下面这些步骤,以便将 dw-ldap-realm.ldif 导入到 Apache Directory Server:

    1. 打开 JXplorer。
    2. 选择 File => Connect 以启动 Open LDAP Connection 对话框(图 15)。
      图 15. JXplorer - 连接到 LDAP
      图 15. JXplorer - 连接到 LDAP
    3. 输入或选择下列值:
      • Host: localhost
      • Port: 10389
      • Protocol:LDAP v3
      • Base DN: ou=system
      • Level:User + Password
      • User DN: uid=admin, ou=system
      • Password: secret
    4. 单击 OK 以连接到 LDAP 服务器。
    5. 选择 LDIF => Import File
    6. 导航到 dw-ldap-realm.ldif 的位置,并选择 Open。这个操作将从该文件导入数据,并显示与图 16 所示类似的面板。
      图 16. JXplorer – 查看数据
      图 16. JXplorer – 查看数据
  3. 创建并测试安全领域

您已经了解了 LDAP 域的先决条件,并创建了所需的构件。在本文的下一个部分中,将会讨论创建安全领域、并使用示例应用程序结合某些高级功能对它进行测试的步骤。


高级功能

前面的部分讨论了一些先决条件,并说明了如何为属性文件域、数据库域、LDAP 域创建所需的构件。您还创建了属性文件域,并使用该域配置了 Web 应用程序的安全性。我们一直推迟到现在才创建数据库和 LDAP 域,这是为了结合某些高级功能来说明这些任务,这些高级功能具体包括:

  • 使用密码摘要来取代明文密码。
  • 支持对所有登录尝试的日志记录进行审核。
  • 在重复失败的登录尝试之后锁定账号。

本部分内容将在您创建数据库域和 LDAP 域时,对这些功能进行研究。

密码摘要和编码

我们没有必要存储原始密码,以使得能够将它们与用户提供的密码进行比较。可以使用消息摘要来比较用户所提供的密码和原始密码,而无需存储原始密码。消息摘要是安全的单向哈希函数,它可以接受任意大小的输入数据,并输出固定长度的二进制哈希值。从密码来计算消息摘要是比较简单的,但以消息摘要为基础来计算密码却是不可行的。Community Edition 允许对属性文件域和数据库域的密码使用摘要。它还允许使用 HEX 和 BASE64 编码,以便将二进制哈希值表示为文本。要创建并使用数据库域(配置为使用密码摘要),您必须完成以下工作:

  1. 生成密码摘要

    为了帮助您为这个示例创建摘要密码,在下载资料中提供了名为 encode.jar 的实用工具。这个实用工具可以为密码生成哈希值。这个实用工具的使用方式是:

    java -jar encode.jar <password> <digest algorithm> <encoding>

    例如,这个命令:

    java -jar encode.jar "hello" "MD5" "hex"

    将采用十六进制的格式输出字符串“hello”的 MD5 摘要。清单 10 提供了我们在这个安全领域中使用的一些密码以及它们的摘要。

    清单 10. 示例编码密码
    Password    MD5 with hex                       SHA-1 with base64	
    --------    ------------                       -----------------
    admindb     a961676177cb4a7c3fb72334600ef72b   HORSE4ibU6JF1xWcPb3vRSDCOek=
    user1       24c9e15e52afc47c225b757e7bee1f9d   s9qne0wEqVUbh4HQMZH+CY8yXmc=
    user2       7e58d63b60197ceb55a1c487989a3720   oYgcBu7JbbmQHHu/5BxCo/COnLQ=
    root        63a9f0ea7bb98050796b649e85481845   3Hbp8MAAbo+RngxRXGbbujmC94U=
    brad1983    b1dde9430b1084ca7514b53a93ec6548   B/ilkJHgVi17TiTcJ3Z5bK8yBFo=
    password    5f4dcc3b5aa765d61d8327deb882cf99   W6ph5Mm5Pz8GgiULbPgzG37mj9g=

    这个安全领域示例使用了十六进制编码的 MD5。

  2. 将数据填充到表中

    您可以通过运行脚本 db-md5-hex-data.sql(在下载资料中提供了该脚本)来为这个示例填充相应的表。这个脚本将使用十六进制格式编码的 MD5 摘要密码来填充数据库。您可以复制该脚本的内容,并将其粘贴到 DB Manager Portlet 的 SQL Command/s 文本域中,然后单击 Run SQL 以填充该数据库。(请确保您选择的是 SAMPLEDB。)

  3. 创建数据库域

    要创建数据库域,需要完成以下工作:

    1. 转到 Security Realms Portlet,并单击 Add new security realm
    2. 对于 Name of the Security Realm,输入 dw-db-realm
    3. 对于 Realm Type,选择 Database (SQL) Realm
    4. 单击 Next 以显示如图 17 中所示的面板。
      图 17. 数据库 (SQL) 域设置
      图 17. 数据库 (SQL) 域设置
    5. 输入下面的值:
      • User SELECT SQL: SELECT username, password FROM users WHERE username=?
      • Group SELECT SQL: SELECT username, groupname FROM groups WHERE username=?
      • Digest Algorithm: MD5
      • Digest Encoding: hex
      • Database Pool: DBRealmPool
    6. 单击 Next 以显示如图 8 中所示的面板。
    7. 单击 Test a Login 以显示如图 9 中所示的面板。
    8. 输入有效的用户名和密码(johnbrad1983),并单击 Login。这时应该显示如图 10 中所示的成功面板。
    9. 单击 Deploy Realm 按钮以部署该安全领域。

    这里,您已经创建了一个数据库域(其密码使用十六进制编码的 MD5 摘要),以及一个用以连接到该数据库的数据库池。

  4. 测试安全领域

    在本文附带提供的下载资料中,包含了您所创建的、配置为根据数据库域进行身份验证的示例应用程序。使用 Deploy New Portlet 部署这个 SimpleWebApp-DB.war 应用程序,然后通过 http://localhost:8080/simplewebappdb/ 访问它。您应该可以使用用户名 dwadmin 和密码 admindb 访问 Admin 页面。请注意,登录对话框显示了 dw-db-realm,正如在应用程序 web.xml 中所配置的一样。另外,验证在应用程序的 SimpleWebApp-DB-plan.xml 部署计划中的 security-realm-name 为“dw-db-realm”。下载资料中包含了这个部署计划,它作为 WebContent\WEB-INF\geronimo-web.xml 打包于 WAR 文件 SimpleWebApp-DB.war 中,因此您不需要在部署时提供计划文件。

审核特性

审核特性允许您配置一个安全领域,以便将所有登录尝试记录到一个文件中。为了在创建安全领域时开启这个功能,请选中 Security Realms Portlet 中 Advanced Configuration 下方的 Auditing 框。要创建并使用支持审核的数据库域,需要完成以下工作:

  1. 创建带有审核功能的数据库域

    回忆一下,您已经在数据库表中填充了数据。要创建数据库域,需要完成以下工作:

    1. 启动 Security Realms Portlet,并单击 Add new security realm
    2. 对于 Name of the Security Realm,输入 dw-db-realm2
    3. 对于 Realm Type,选择 Database (SQL) Realm,并单击 Next
    4. 在 Configure Login Module 面板(图 18)中,输入或者选择下面的值:
      • User SELECT SQL: SELECT username, password FROM users WHERE username=?
      • Group SELECT SQL: SELECT username, groupname FROM groups WHERE username=?
      • Digest Algorithm: MD5
      • Digest Encoding: hex
      • Database Pool:保留空白,因为您将直接使用 JDBC。
      • JDBC Driver Class: org.apache.derby.jdbc.ClientDriver
      • Driver JAR:org.apache.derby/derbyclient/10.2.2.0/jar
      • JDBC URL: jdbc:derby://localhost:1527/SAMPLEDB
      • JDBC Username: APP
      • JDBC Password 和 Confirm Password: password
      单击 Next
      图 18. 数据库(SQL)域 - JDBC 设置
      图 18. 数据库(SQL)域 - JDBC 设置
    5. 在 Advanced Configuration 面板(图 19)中,选中 Enable Auditing;对于 Log File,输入 var/log/login-attempts.log
      图 19. 启用审核功能
      图 19. 启用审核功能
    6. 单击 Test a Login 以显示如图 9 中所示的面板。
    7. 输入一个有效的用户名和密码(johnbrad1983)并单击 Login。这个操作将显示成功页面,如图 10 中所示。
    8. 单击 Deploy Realm 以部署该安全领域。

    您现在已经创建了一个使用 MD5 摘要密码(使用十六进制编码)的数据库域,使用 JDBC 连接到数据库,并且支持审核功能。

  2. 测试安全领域

    在本文所提供的下载资料中,包括一个配置为根据数据库域(上面创建的、支持审核功能的数据库域)进行身份验证的示例应用程序。使用 Deploy New Portlet 部署这个 SimpleWebApp-Audit.war 应用程序,并通过 http://localhost:8080/simplewebappaudit/ 访问该应用程序。您应该可以使用用户名 dwadmin 和密码 admindb 访问 Admin 页面,但是在您使用正确的登录信息访问 Admin 页面之前,请先输入错误的登录信息以观察记录到 <WASCE_HOME>/var/log/login-attempts.log 中的失败登录。现在,提供正确的登录信息,以便对 Admin 页面进行访问。再次验证已经将成功的登录记录到了 <WASCE_HOME>/var/log/login-attempts.log。

锁定特性

如果在指定的时间范围内出现了指定次数的失败登录,那么锁定特性允许您配置对特定用户名进行锁定,并持续一段时间(称为锁定持续时间)。在指定次数的失败登录尝试之后,如果用户为该用户名提供了正确的凭据,Community Edition 仍然将报告登录失败,直到锁定持续时间结束(从最后一次失败登录尝试开始计时)。本文将结合 LDAP 域来说明锁定特性。要创建并使用 LDAP 域(配置了锁定特性),需要完成以下工作:

  1. 创建带有锁定功能的 LDAP 域

    清单 11 显示了 dw-ldap-realm.ldif 文件的摘录,在本文早些时候曾使用该文件向目录服务器添加数据。这个清单显示了用户名为“john”的用户和组名为“Admin”的组的数据。User 之下重要的条目是“dn”、“uid”和“userPassword”,Group 之下重要的条目是“dn”、“uniqueMember”和“cn”。当您在 LDAP 域(您将在下一个步骤中创建它)中配置相关选项的时候,将涉及到这些条目。

    清单 11. dw-ldap-realm.ldif
    # User: john
    
    dn: uid=john,ou=users,ou=system
    cn: John Smith
    sn: John
    givenname: John
    objectclass: top
    objectclass: person
    objectclass: organizationalPerson
    objectclass: inetOrgPerson
    ou: Human Resources
    ou: People
    l: Las Vegas
    uid: john
    mail: john@dw.com
    telephonenumber: +1 408 555 5555
    facsimiletelephonenumber: +1 408 555 5556
    roomnumber: 4613
    userPassword: brad1983
    
    # Group: Admin
    
    dn: cn=Admin,ou=groups,ou=system
    objectClass: groupOfUniqueNames
    uniqueMember: uid=dwadmin,ou=users,ou=system
    uniqueMember: uid=root,ou=users,ou=system
    cn: Admin

    要创建一个配置为使用 Apache Directory Server(作为插件安装于 Community Edition 中)的 LDAP 域,需要完成以下工作:

    1. 启动 Security Realms Portlet,并单击 Add new security realm
    2. 对于 Name of the Security Realm,输入 dw-ldap-realm
    3. 对于 Realm Type,选择 LDAP Realm,并单击 Next
    4. 在 Configure Login Module 面板(图 20)中 ,输入或选择下列值:
      • Initial Context Factory: com.sun.jndi.ldap.LdapCtxFactory
      • Connection URL:ldap://localhost: 10389
      • Connect Username: uid=admin,ou=system
      • Connect Password 和 Confirm Password: secret
      • Connect Protocol:保留空白
      • Authentication: simple
      • User Base: ou=users,ou=system
      • User Search Matching: uid={0}
      • User Search Subtree: false
      • Role Base: ou=groups,ou=system
      • Role Name: cn
      • Role User Search String: (uniqueMember={0})
      • Role Search Subtree: false
      • User Role Search String:保留空白。
      上面列出了连接到目录服务器时所使用的前七个字段。回忆一下,这些字段值与您使用 JXplorer 连接到目录服务器时所使用的字段值是相同的。接下来的八个字段,从 User Base 到 Role Search String,用于根据存储于目录服务器中的数据对用户进行身份验证,并为用户获取组信息。回忆一下前面的清单 11,每个用户都有一个 uid=<username>,ou=users,ou=system 形式的“dn”,且每个组都有一个 cn=<groupname>,ou=groups,ou=system 形式的“dn”。这说明了 User Base、User Search Matching、Role Base 和 Role Name 字段的值。再回忆一下前面的清单 11,对于一个组中的每个成员,都有一个 uniqueMember 条目。该条目说明了 Role User Search String 字段的值。
      图 20. LDAP 域设置
      图 20. LDAP 域设置

      单击 Next

    5. 在 Advanced Configuration 面板中,选中 Enable Lockout,然后分别在接下来的三个字段中输入 36030(图 21)。
      图 21. 启用锁定
      图 21. 启用锁定
    6. 单击 Test a Login 以显示与图 9 所示类似的面板。
    7. 输入用户名 dwadmin 和密码 adminldap,然后单击 Next。这时将显示与图 10 所示类似的 Login Results 面板。
    8. 单击 Deploy Realm

    这个操作将完成 LDAP 域的创建。您还配置了锁定特性,这使得如果在 60 秒之内某用户连续出现 3 次失败的登录尝试,那么就将该用户锁定 30 秒。

  2. 测试安全领域

    在本文所提供的下载资料中,包含您刚刚创建的、配置为根据 LDAP 域进行身份验证的示例应用程序。使用 Deploy New Portlet 部署这个 SimpleWebApp-LDAP.war 应用程序,并通过 http://localhost:8080/simplewebappldap/ 访问该应用程序。您应该可以使用用户名 dwadmin 和密码 adminldap 访问 Admin 页面。

    要验证锁定特性,需要完成以下工作:

    1. 如果您已经登录,那么使用 Logout 链接从应用程序中注销。
    2. 访问 http://localhost:8080/simplewebappldap/admin/admin.jsp。
    3. 在 Login 页面中,输入用户名 dwadmin,但不要输入密码,然后单击 Login。这时应该显示一条登录错误信息。
    4. 单击您的浏览器中的 Back 按钮,以返回到 Login 页面。再次输入用户名 dwadmin,但不要输入密码,并单击 Login。这次登录将被登记为第二次失败的登录尝试。
    5. 重复步骤 d 以登记第三次失败的登录尝试。
    6. 再次单击 Back 按钮。这一次,在 Login 页面中输入正确的登录信息(Username:dwadmin、Password:adminldap),并再次单击 Login。这个操作仍然应该返回一条登录错误信息。
    7. 现在,等待至少 30 秒,然后重复步骤 f。这次您将能够成功地登录。

    刚刚发生的是:您启用了锁定特性,如果在 60 秒之内出现 3 次登录失败尝试后,会将该帐号锁定 30 秒。因此,执行到步骤 e 时锁定了 dwadmin 帐号,所以在步骤 f 中,尽管您提供了正确的登录信息,但该次登录仍然失败。当 30 秒的时间过去之后,该帐号将解除锁定,因此在步骤 g 中您可以成功地登录。

存储凭据

您可以采用两种方式配置安全领域,以便存储对用户进行身份验证的凭据:

  • 存储密码

    通常,在对用户进行了身份验证之后,应用程序并不需要访问用户的密码。如果必须这样做的话,那么 Community Edition 将允许您配置一个安全领域以存储用户的密码。在您创建安全领域的时候,只需要选择 Store Password 选项就可以完成这项任务(图 8)。当启用了这个选项之后,安全领域将在该主题的私有凭据中存储对用户进行身份验证的用户名和密码,从而允许在完成登录过程之后能够访问该密码。在这个示例中,Community Edition 使用 GeronimoPasswordCredential 对象存储用户的数据。

  • 命名凭据

    命名凭据的特性允许您配置需要存储的凭据名称以及密码。同样,在创建安全领域的时候,只需要在 Advanced Configuration 面板中选择 Named Credential 选项就可以完成这个任务(图 8)。在启用了该特性之后,安全领域将在该主题中指定凭据名称下的私有凭据中存储对用户进行身份验证的用户名和密码。在这个示例中,Community Edition 使用 NamedUsernamePasswordCredential 对象存储用户的数据。

    在从应用程序内部调用 Web 服务的时候,通常使用命名凭据的特性来传递凭据。在本文稍后的应用程序范围安全领域部分中,通过一个示例应用程序说明了如何从主题中检索这些私有凭据。(说明 Web 服务调用超出了本文的范围。)

域主体和登录域主体

通常,对于两个具有相同名称和相同主体类、但却由两个不同的 LoginModule 生成的不同主体,Community Edition 无法进行区分。如果启用了 wrap-principals 选项,Community Edition 将对主体进行“包装”,以跟踪各个主体来自于哪个登录模块和安全领域。通过在 login-module 中将 wrap-principals 属性设置为“true”(清单 12)可以完成这个任务。这样做允许您使用 Community Edition 部署计划安全映射中的 realm-principal 和 login-domain-principal 元素。接下来,将通过一个应用程序范围的安全领域来说明 realm-principal 和 login-domain-principal 的使用。

应用程序范围的安全领域

还可以将安全领域部署为使用该安全领域的 Web 应用程序的一部分。通过将安全领域 GBean 定义添加到应用程序的部署计划,可以完成这项任务。通常,将这种类型的安全领域称为应用程序范围的安全领域。在本文中到目前为止,您已经创建了所有的安全领域,并且它们都在自己的配置中运行,而它们的范围都被认为是服务器范围,这在 Security Realms Portlet 中的“Deployed as”列中进行了描述(图 11)。

  1. 研究示例应用程序

    在本文所提供的下载资料中,包括一个示例应用程序 SimpleWebApp-Subject.war,它使用了一个作为应用程序的一部分而部署的安全领域。请注意,这个应用程序的部署计划 SimpleWebApp-Subject-plan.xml(清单 12),使用一个名为 dw-prop-realm 的 GBean 来定义安全领域,这是一个属性文件域,它使用了 dw_users.properties 和 dw_groups.properties。

    清单 12. SimpleWebApp-Subject 的部署计划
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://geronimo.apache.org/xml/ns/j2ee/web-2.0">
      ...
      <context-root>simplewebappsubject</context-root>
      <security-realm-name>dw-prop-realm</security-realm-name>
      <security>
        <role-mappings>
          <role role-name="admin">
            <realm-principal name="john" domain-name="dw-prop"
              realm-name="dw-prop-realm"
     class="org.apache.geronimo.security.realm.providers.GeronimoUserPrincipal" />
            <principal name="Admin"
     class="org.apache.geronimo.security.realm.providers.GeronimoGroupPrincipal" />
          </role>
          <role role-name="user">
            <login-domain-principal name="john"
              domain-name="unknown"
     class="org.apache.geronimo.security.realm.providers.GeronimoUserPrincipal" />
            <principal name="User"
     class="org.apache.geronimo.security.realm.providers.GeronimoGroupPrincipal" />
          </role>
        </role-mappings>
      </security>
      <gbean name="dw-prop-realm"
        class="org.apache.geronimo.security.realm.GenericSecurityRealm"
        xsi:type="dep:gbeanType"
        xmlns:dep="http://geronimo.apache.org/xml/ns/deployment-1.2"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <attribute name="realmName">dw-prop-realm</attribute>
        <reference name="ServerInfo">
          <name>ServerInfo</name>
        </reference>
        <xml-reference name="LoginModuleConfiguration">
          <log:login-config
            xmlns:log="http://geronimo.apache.org/xml/ns/loginconfig-2.0">
            <log:login-module control-flag="REQUIRED"
              wrap-principals="true">
              <log:login-domain-name>dw-prop</log:login-domain-name>
              <log:login-module-class>
                org.apache.geronimo.security.realm.providers.PropertiesFileLoginModule
              </log:login-module-class>
              <log:option name="groupsURI">
                var/security/dw_groups.properties
              </log:option>
              <log:option name="usersURI">
                var/security/dw_users.properties
              </log:option>
            </log:login-module>
            <log:login-module control-flag="OPTIONAL"
              wrap-principals="false">
              <log:login-domain-name>
                dw-prop-realm-Password
              </log:login-domain-name>
              <log:login-module-class>
           org.apache.geronimo.security.realm.providers
            .GeronimoPasswordCredentialLoginModule
              </log:login-module-class>
            </log:login-module>
            <log:login-module control-flag="OPTIONAL" wrap-principals="false">
              <log:login-domain-name>
                dw-prop-realm-NamedUPC
              </log:login-domain-name>
              <log:login-module-class>
                org.apache.geronimo.security.jaas.NamedUPCredentialLoginModule
              </log:login-module-class>
              <log:option
                name="org.apache.geronimo.jaas.NamedUPCredentialLoginModule.Name">
                mycredname
              </log:option>
            </log:login-module>
          </log:login-config>
        </xml-reference>
      </gbean>
    </web-app>

    在这个部署计划中:

    • 安全领域 GBean 是应用程序部署计划自身的一部分。
    • 将 PropertiesFileLoginModule 的 wrap-principals 属性设置为“true”。这一操作的结果是将 RealmPrincipal 和 LoginDomainPrincipal 添加到除了原始主体以外的每个 GeronimoUserPrincipal 和 GeronimoGroupPrincipal 的主题。
    • 在 role-mapping 中,login-domain-principal 和 realm-principal 中的 domain-name 和 realm-name 属性的值分别来自于 login-module 中的 login-domain-name 和 GBean 中的realmName 属性。在这个示例中,这些值是“dw-prop”和“dw-prop-realm”。
    • 这两个附加的 LoginModules GeronimoPasswordCredentialLoginModule 和 NamedUPCredentialLoginModule 对应于前面讨论过的存储密码和命名凭据的特性。

    可以使用 PolicyContext.getContext() 方法,在 Servlet 或者 JSP 中检索包含对用户进行身份验证的主体和凭据的主题。示例应用程序中的 Admin 页面显示了该主题中的所有主体和凭据。清单 13 显示了 admin.jsp 的摘录。

    清单 13. 用于显示主体和凭据的 JSP
    <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
        pageEncoding="ISO-8859-1"%>
    <%@ page import="javax.security.auth.Subject" %>
    <%@ page import="javax.security.jacc.PolicyContext" %>
    <%@ page import="o.a.geronimo.security.realm.providers.GeronimoPasswordCredential" %>
    <%@ page import
     ="org.apache.geronimo.security.jaas.NamedUsernamePasswordCredential" %>
    <html>
    ...
    <br>Welcome <b><%=request.getUserPrincipal().getName() %></b> !!
    <br>
    <br>You are <%=request.isUserInRole("admin") ? "in" : "not in"%> 'admin' role.
    <br>You are <%=request.isUserInRole("user") ? "in" : "not in"%> 'user' role.
    <br>
    ...
    <%  // This works for any JEE Server using JACC
    Subject subject =
     (Subject)PolicyContext.getContext("javax.security.auth.Subject.container");
    %>
    <%-- The following code displays the contents of the Subject --%>
    <br><%=subject.getPrincipals().size()%> Principals
    <%for(java.security.Principal p:subject.getPrincipals()) { %>
        <br>* <%=p.getClass().getName()%> : <%=p.getName()%>
    <%}%>
    <hr>
    <br><%=subject.getPrivateCredentials().size()%> Private Credentials
    <%for(Object c:subject.getPrivateCredentials()) { %>
        <br><%=c%>
    <%if(c instanceof GeronimoPasswordCredential) {%>
        <br>Username: <%=((GeronimoPasswordCredential)c).getUserName()%>
        <br>Password: <%=new String(((GeronimoPasswordCredential)c).getPassword())%>
    <%}%>
    <%if(c instanceof NamedUsernamePasswordCredential) {%>
        <br>Credential Name: <%=((NamedUsernamePasswordCredential)c).getName()%>
        <br>Username: <%=((NamedUsernamePasswordCredential)c).getUsername()%>
        <br>Password: <%=new String(((NamedUsernamePasswordCredential)c).getPassword())%>
    <%}%>
    <hr>
    <%}%>
    <br><%=subject.getPublicCredentials().size()%> Public Credentials
    <%for(Object c:subject.getPublicCredentials()) { %>
        <br><%=c%>
    <%}%>
    ...
    </html>

    使用 Subject.getPrincipal() 方法检索该主题中的主体。可以使用 Subject.getPrivateCredentialsMethod() 检索私有凭据,可以使用 Subject.getPublicCredentail() 方法检索公共凭据。

  2. 测试安全领域

    1. 使用 Deploy New Portlet 安装示例应用程序 SimpleWebApp-Subject.war 。
    2. 现在,访问 Security Realms Portlet。请注意,该 Portlet 在 dw-prop-realm 的“Deployed as”列中显示了 dw/SimpleWebApp-Subject/1.0/war。
    3. 通过 http://localhost:8080/simplewebappsubject 访问该应用程序,并使用用户 ID john 和密码 brad1983 登录到 Admin 页面。请注意,Admin 页面显示了在该主题中的主体、私有凭据和公共凭据。访问 User 页面将产生一条错误消息“HTTP Status 403”。

    这里列出了一些重要的观察结果:

    • 在 admin 角色的 role-mapping 中,使用下面几项内容来指定 realm-principal:
      • name="john"
      • realm-name="dw-prop-realm"
      • domain-name="dw-prop"
      因为当“john”登录的时候,这三个值可以匹配该主题中的 RealmPrincipal 将包含的内容,所以这个用户将可以访问 Admin 页面。
    • 在 user 角色的 role-mapping 中,使用下面几项内容来指定 login-domain-principal:
      • name="john"
      • domain-name="unknown"
      但是当“john”登录的时候,LoginDomainPrincipal 将包含下面的内容:
      • name="john"
      • domain-name="dw-prop"
      尽管名称是匹配的,但是 domain-names 却并不匹配,因此“john”将不能访问“User”页面。
    • 该主题包含对应于每个 GeronimoUserPrincipal/GeronimoGroupPrincipal 的 RealmPrincipal 和 LoginDomainPrincipal。另外,还有两个私有凭据:GeronimoPasswordCredential 和 NamedUsernamePasswordCredential。

结束语

IBM WebSphere Application Server Community Edition V2.0 提供了 JAAS LoginModules 实现,可以很容易地将该实现用于创建允许在各种数据存储(如属性文件、数据库表和 LDAP 服务器)中存储用户凭据信息的各种安全领域。此外,Community Edition 还提供了一些高级安全特性,比如使用摘要密码、审核和对重复的失败登录进行锁定。本文说明了如何在 Community Edition 内部运行所需的基础结构,以及如何使用这些安全领域来配置 Web 应用程序安全性。


下载

描述名字大小
Code sampleswascesecurity_samples.zip60 KB

参考资料

学习

获得产品和技术

讨论

条评论

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
ArticleID=263073
ArticleTitle=在 WebSphere Application Server Community Edition V2.0 中配置 Web 应用程序安全性
publish-date=10182007