安全指南

JBoss 企业级应用程序平台 6.1

适用于 JBoss 企业版应用程序平台 6

版 1

Sande Gilda

David Ryan

Misty Stanley-Jones

摘要

本书是 JBoss 企业版应用程序平台 6 及其补丁版本的安全指南。

Preface

部分 I. JBoss 企业版应用程序平台 6 的安全性

第 1 章 简介

1.1. 关于安全性

计算机安全是信息技术领域的一个笼统术语,它处理数字时代的虚拟环境的安全性 。它可以包含数据保护和完整性、应用程序安全性、风险和漏洞评估、验证和授权协议。
对于多数机构来说,计算机数据是非常重要的资产。数据保护是至关重要的,它组成多数商业的核心。JBoss EAP 提供了一个多层次的途径在所有阶段来保护数据。
真正安全的系统是那些从一开始就以安全性为主要功能来设计的系统。这样的系统使用 Security by Design 原则。在这样的系统里,恶意攻击和渗透都是作为安全设施的一部分被接受的,系统会有计划地绕开它们。
安全性可以应用在操作系统、中间件和应用程序级别。关于操作系统级别的安全性的更多信息,请参考下列文档:https://access.redhat.com/knowledge/docs/en-US/Red_Hat_Enterprise_Linux/6/html-single/Security_Guide/index.html
在后面的章节里,你将了解 JBoss EAP 安全性的不同级别和层次。这些层次为平台里所有的安全格功能提供了基础结构。

1.2. 安全性和系统管理员

系统管理员负责计算机系统和网络,他必须精于处理网络攻击,而且必须通过有计划的安全演练和审计来主动阻拦这些攻击。
对于成功的系统管理员而言,针对安全性攻击作好计划是艺术和科学的结合。安全性威胁,不管是物理的还是基于网络或数据的,本质上会有不同,而成功的安全系统管理员可以为段供和冗余都做好准备。
系统管理专家,或安全管理员不负责中间件或应用程序的安全性而只负责系统和网络级别的安全性。

1.3. 安全性和 J2EE 开发人员

应用程序级别的安全性是 J2EE 开发人员的职责,而这可以分成三个独立的角色:
  • 应用程序开发人员 - 负责开发级别的安全性以及定义角色、规则和商业逻辑到应用程序逻辑。
  • 应用程序组装者 - 负责确保完成 EAR 和 WAR 的打包,将跨应用程序的易损性降至最低。
  • 应用程序部署者 - 负责保护 EAR 的部署且分配和维护访问控制列表。
这三个角色由同一组开发人员担任是很常见的。
JBoss EAP,作为一个组件平台,提供了声明式安全性。并非将安全性逻辑内嵌到商业组件里,你是用标准的 XML 描述符来描述安全性角色和权限。用这种方式,商业级别的代码和安全性代码隔离开来。关于 JBoss EAP 里的声明式安全性的更多内容,请参阅 第 2.4 节 “关于声明式安全性”
声明式安全性是由程序式安全性支持的。J2EE 开发人员可以在代码里使用 J2EE API 来确定授权并执行加强的安全性。

部分 II. 保证平台的安全

第 2 章 安全子系统

2.1. 关于安全子系统

安全子系统为 JBoss EAP 里的所有安全功能提供了基础结构。多数配置元素都几乎不需要修改。唯一需要修改的配置元素是是否使用 deep-copy-subject-mode。此外,你可以配置系统范围的安全属性。多数的配置都和安全域(Security Domain)相关。
Deep Copy 模式

如果 Deep Copy 模式被禁用(默认),复制安全数据结构会产生一个对原始结构的引用,而不是复制整个数据结构。这个行为效率更高,但在具有相同标识符的多个线程通过冲刷或登出操作清除主题时容易受数据损坏的影响。

只要被标记为可克隆的(cloneable),Deep Copy 模式会导致数据结构及相关数据的完整复制。这是更多线程安全的,但效率更低。
系统范围的安全属性

你可以设置系统范围的安全属性,它们应用在 java.security.Security class

安全域

安全域(Security Domain)是一系列 Java 验证和授权服务(Java Authentication and Authorization Service,JAAS)的声明式安全配置,一个或多个应用程序用它来控制验证、授权、审计和映射。有三个默认的安全域:jboss-ejb-policyjboss-web-policyother。你也可以按照应用程序的需要创建安全域。

2.2. 关于安全子系统的结构

安全子系统是在受管域或独立服务器的配置文件里配置的。多数配置元素可以用基于 web 的管理控制台或基于控制台的管理 CLI 来配置。下面是一个安全子系统配置的 XML 片段。

例 2.1. 安全子系统配置示例

<subsystem xmlns="urn:jboss:domain:security:1.2">
	<security-management>
		...
	</security-management>
	<security-domains>
        <security-domain name="other" cache-type="default">
            <authentication>
                <login-module code="Remoting" flag="optional">
                    <module-option name="password-stacking" value="useFirstPass"/>
                </login-module>
                <login-module code="RealmUsersRoles" flag="required">
                    <module-option name="usersProperties" value="${jboss.domain.config.dir}/application-users.properties"/>
                    <module-option name="rolesProperties" value="${jboss.domain.config.dir}/application-roles.properties"/>
                    <module-option name="realm" value="ApplicationRealm"/>
                    <module-option name="password-stacking" value="useFirstPass"/>
                </login-module>
            </authentication>
        </security-domain>
        <security-domain name="jboss-web-policy" cache-type="default">
            <authorization>
                <policy-module code="Delegating" flag="required"/>
            </authorization>
        </security-domain>
        <security-domain name="jboss-ejb-policy" cache-type="default">
            <authorization>
                <policy-module code="Delegating" flag="required"/>
            </authorization>
        </security-domain>
    </security-domains>
    <vault>
    	...
    </vault>
</subsystem>		
		

<security-management><subject-factory><security-properties> 元素没有出现在默认配置里。从 JBoss EAP 6.1 开始已启用了 <subject-factory><security-properties> 元素。

2.3. 关于加密

加密(Encryption)指的是应用数学算法来模糊敏感信息。加密是保护你的基础结构免受数据泄露、系统断供和其他风险的基本措施之一。
加密可以应用在简单的字符串数据上、如密码。它也可以应用到数据通讯流。例如,HTTPS 协议在传输数据前会加密所有的数据。如果你用 SSH 协议连接两个服务器,所有的通讯数据都是以加密的 通道(tunnel) 发送的。

2.4. 关于声明式安全性

声明式安全性(Declarative security)是一个通过容器管理安全性来从应用程序代码里分离安全性。容器提供一个基于文件权限或用户、组和角色的授权系统。这个方法通常比让应用程序完全负责安全性的编程式(programmatic)安全性更好。
JBoss EAP 通过安全域来提供声明式安全性。

2.5. 配置安全子系统

你可以用管理 CLI 或基于 web 的管理控制台来配置安全子系统。
安全子系统里的每个顶级元素都包含关于安全配置不同方面的信息。关于安全子系统配置的例子,请参考 第 2.2 节 “关于安全子系统的结构”
<security-management>
这部分内容覆盖了安全子系统的高层行为。每个设置都是可选的。除了 Deep Copy 模式,须该这些设置的任何一个都是不寻常的。
选项 描述
deep-copy-subject-mode
指定是否复制或链接安全令牌以用于额外的线程安全。
authentication-manager-class-name
指定一个要使用的其他的 AuthenticationManager 实现的类名。
default-callback-handler-class-name
指定和登录模块一起使用的 CallbackHandler 实现的全局类名。
authorization-manager-class-name
指定一个要使用的其他的 AuthorizationManager 实现的类名。
audit-manager-class-name
指定一个要使用的其他的 AuditManager 实现的类名。
identity-trust-manager-class-name
指定一个要使用的其他的 IdentityTrustManager 实现的类名。
mapping-manager-class-name
指定一个要使用的 MappingManager 实现的类名。
<subject-factory>
主题工厂(Subject factory)控制主题实例的创建。它可以使用验证管理者来检验调用者。主题工厂的主要用途是为了 JCA 组件建立主题。你通常不需要修改它。
<security-domains>
保存多个安全域的容器元素。安全域可能包含关于验证、授权、映射、审计模块以及 JASPI 验证和 JSSE 配置的信息。你的应用程序可以指定一个安全域来管理它的安全信息。
<security-properties>
包含在 java.security.Security 类上设置的属性的名字和值。

第 3 章 管理接口的安全性

3.1. 保护管理接口

总结

在测试环境里,我们通常在由管理控制台、管理 CLI 和其他 API 实现组成的管理接口上不设置安全层来运行 JBoss EAP 6 。这可以允许快速的开发和配置修改。

此外,silent 验证模式也是默认的,这允许主机上的本地客户可以连接管理 CLI 而无需用户名和密码。这对于本地用户和管理 CLI 脚本是很方便的,但它可以根据需要禁用。其步骤请参考 。
当你开始测试并准备移至产品环境时,至少通过下列方式保护管理接口是极其重要的:

3.2. 默认的用户安全性配置

简介

在 EAP 6 里,所有的管理接口都默认是有设置安全性的。这个安全性采取两种形式:

  • 本地接口通过本地客户和服务器间的 SASL 合约设置安全性。这个安全机制基于客户访问本地文件系统的能力。这是因为访问本地文件系统会允许客户添加用户或修改配置以阻挠其他安全机制。如果对文件系统的物理访问可以实现,那么其他安全机制就是多余的。这个机制以四个步骤实现:

    注意

    即使你通过 HTTP 连接本地主机,HTTP 访问仍会视作远程的。
    1. 客户发送一条消息给服务器,它包含一个用本地 SASL 机制验证的请求。
    2. 服务器生成一个一次性的令牌,将其写入到唯一的文件里,然后发送具有完整文件路径的消息给客户。
    3. 客户从文件里读取令牌并发送给服务器,检验它是否具有对文件系统的本地访问权限。
    4. 服务器验证令牌并删除这个文件。
  • 远程客户,包括本地 HTTP 客户,使用基于区的安全性。带有使用管理接口远程配置 EAP 权限的默认区是 ManagementRealm。我们提供了一个脚本,允许你添加用户到这个区(或者你创建的区)。请参考《JBoss EAP 6 安装指南》的 Getting Started 章节。对于每个用户,用户名,hased 密码,以及区都存储在文件里。
    独立服务器
    JPP_HOME/standalone/configuration/mgmt-users.properties
    即使注明了 mgmt-users.properties 的内容,这个文件仍应该作为敏感文件对待。我们推荐将其文件权限设置为 600,这样除了文件所有者,其他人都没有读或写的权限。

3.3. 高级管理接口配置概述

EAP_HOME/domain/configuration/host.xmlEAP_HOME/standalone/configuration/standalone.xml 里的管理接口配置控制主机控制器进程绑定哪些网络接口,哪种管理接口是可用的,哪种类型的验证系统用来验证每个接口上的用户。本主题讨论了如何配置管理接口以适应你的运行环境。
管理子系统由包含几个可配置属性以及下面三个可配置子元素的 <management> 元素组成。首先定义安全区和转出连接,然后再将它们作为属性应用到管理接口。
  • <security-realms>
  • <outbound-connections>
  • <management-interfaces>
安全区

安全区(Security Realm)负责允许通过管理 API、管理 CLI 和基于 web 的管理控制台管理 JBoss EAP 的用户的验证和授权。

默认安装里有两种不同的基于文件的安全区:ManagementRealmApplicationRealm。每个安全区都使用一个 -users.properties 文件来存储用户和哈希密码,以及一个 -roles.properties 来存储用户和角色间的映射。相同也包含了对启用了 LDAP 的安全区的支持。

注意

安全区也可以用于你自己的应用程序。这里讨论的安全区是管理接口所专有的。
转出的连接

一些安全区连接至外部接口,如 LDAP 服务器。转出连接(Outbound connection)定义了如何创建这种连接。预定义的连接类型,ldap-connection,设置了连接 LDAP 服务器并验证凭证的所有必需和可选的属性。

管理接口

管理接口包含如何连接和配置 JBoss EAP 的属性。这样的信息包括命名网络接口、端口、安全区和其他关于接口的可配置信息。默认安装里包含了两个接口:

  • http-interface 是基于 web 的管理控制台的配置。
  • native-interface 是命令行管理 CLI 和类 REST 管理 API 的配置。
这三个主机管理子系统的主要可配置元素都是相关的。安全区引用转出连接,而管理接口引用安全区。

3.4. 禁用 HTTP 管理接口

在受管域里,你只需要访问域控制器而不是域成员服务器上的 HTTP 接口。此外,在产品服务器上,你可能会决定禁用基于 web 的管理控制台。

注意

其他服务器,如 JBoss Operations Network,也使用 HTTP 接口来操作。如果你想使用这些服务,简单地禁用管理控制台自身就可以了,你可以设置 HTTP 接口的 console-enabled-attributefalse,而无需完全禁用这个接口。
/host=master/core-service=management/management-interface=http-interface/:write-attribute(name=console-enabled,value=false)
要禁用对 HTTP 接口的访问,同时也禁用对基于 Web 的管理控制台的访问,你可以将 HTTP 接口一起删除。
如果你又想再次添加这个接口,下面的 JBoss CLI 命令允许你读取 HTTP 接口的当前内容。

例 3.1. 读取 HTTP 接口的配置

/host=master/core-service=management/management-interface=http-interface/:read-resource(recursive=true,proxies=false,include-runtime=false,include-defaults=true)
{
    "outcome" => "success",
    "result" => {
        "console-enabled" => true,
        "interface" => "management",
        "port" => expression "${jboss.management.http.port:9990}",
        "secure-port" => undefined,
        "security-realm" => "ManagementRealm"
    }
}
要删除 HTTP 接口,请执行下列命令:

例 3.2. 删除 HTTP 接口

/host=master/core-service=management/management-interface=http-interface/:remove
要重新启用访问,执行下列命令来重新创建带有默认值的 HTTP 接口。

例 3.3. 重新创建 HTTP 接口

/host=master/core-service=management/management-interface=http-interface/:write-attribute(name=console-enabled,value=true)
/host=master/core-service=management/management-interface=http-interface/:write-attribute(name=interface,value=management)
/host=master/core-service=management/management-interface=http-interface/:write-attribute(name=port,value=${jboss.management.http.port:9990})
/host=master/core-service=management/management-interface=http-interface/:write-attribute(name=security-realm,value=ManagementRealm)

3.5. 从默认的安全区删除无提示验证

总结

JBoss EAP 6 的默认安装包含一个用于本地管理 CLI 用户的无提示验证(Silent Authentication)方法。这允许本地用户无需用户名或密码验证就可以访问管理 CLI。启用这个功能是为了方便,并协助本地用户无需验证就可以运行管理 CLI 脚本。它是一个非常有用的功能,特别是对本地配置的访问通常也会让用户可以添加自己的细节或禁用安全检查。

如果需要更严格的安全检查,你也可以禁用对于本地用户的无提示验证。这可以通过删除配置文件里的 security-realm 部分里的 local 来实现。这适用于独立服务器的 standalone.xml 或受管域的 host.xml。你应该只在理解了对特定服务器配置的影响后才考虑删除 local 元素。
删除无提示验证的首选方法是使用管理 CLI,在下面的例子里它直接删除了 local 元素。

例 3.4. security-realm 里的 local 元素示例

<security-realms>
    <security-realm name="ManagementRealm">
        <authentication>
            <local default-user="$local"/>
            <properties path="mgmt-users.properties" relative-to="jboss.server.config.dir"/>
        </authentication>
    </security-realm>
    <security-realm name="ApplicationRealm">
        <authentication>
            <local default-user="$local" allowed-users="*"/>
            <properties path="application-users.properties" relative-to="jboss.server.config.dir"/>
        </authentication>
        <authorization>
            <properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
        </authorization>
    </security-realm>
</security-realms>

过程 3.1. 从默认的安全区删除无提示验证

  • 用管理 CLI 删除无提示验证

    按要求从管理区和应用程序区删除 local 元素。
    1. 从管理区删除 local 元素。
      • 对于独立服务器

        /core-service=management/security-realm=ManagementRealm/authentication=local:remove
      • 对于受管域

        /host=HOST_NAME/core-service=management/security-realm=ManagementRealm/authentication=local:remove
    2. 从应用程序区删除 local 元素。
      • 对于独立服务器

        /core-service=management/security-realm=ApplicationRealm/authentication=local:remove
      • 对于受管域

        /host=HOST_NAME/core-service=management/security-realm=ApplicationRealm/authentication=local:remove
结果

无提示验证从 ManagementRealmApplicationRealm 里删除了。

3.6. 禁用对 JMX 子系统的远程访问

远程 JMX 连接性允许你触发 JDK 和应用程序管理操作。为了保护安装,请禁用这个功能。你可以通过删除远程连接配置或完全删除 JMX 子系统来实现这一点。JBoss CLI 命令引用了受管域配置里的 default 配置集。要进行修改,请修改命令的 /profile=default 部分。对于独立服务器,请完全删除命令的这个部分。

注意

在受管域里,远程连接器默认是从 JMX 子系统里删除的。如果是在部署时添加的,这个命令可供你参考。

例 3.5. 从 JMX 子系统删除远程连接器。

/profile=default/subsystem=jmx/remoting-connector=jmx/:remove

例 3.6. 删除 JMX 子系统

如果你使用了受管域,对你使用的每个配置集都运行这个命令。
/profile=default/subsystem=jmx/:remove

3.7. 为管理接口配置安全区

管理接口使用安全区来控制验证和对 JBoss EAP 的配置机制的访问。本主题展示了如何阅读和配置安全区。这些命令使用了管理 CLI。
读取安全区的配置

这个例子展示了 ManagementRealm 安全区的默认配置。它使用了一个名为 mgmt-users.properties 的文件来保存其配置信息。

例 3.7. 默认的 ManagementRealm

	/host=master/core-service=management/security-realm=ManagementRealm/:read-resource(recursive=true,proxies=false,include-runtime=false,include-defaults=true)
{
    "outcome" => "success",
    "result" => {
        "authorization" => undefined,
        "server-identity" => undefined,
        "authentication" => {"properties" => {
            "path" => "mgmt-users.properties",
            "plain-text" => false,
            "relative-to" => "jboss.domain.config.dir"
        }}
    }
}
编写安全区

下面的命令创建了一个名为 TestRealm 的安全区并为相关的配置文件设置名称和目录。

例 3.8. 编写安全区

/host=master/core-service=management/security-realm=TestRealm/:add/host=master/core-service=management/security-realm=TestRealm/authentication=properties/:add(path=TestUsers.properties, relative-to=jboss.domain.config.dir)

应用安全区到管理接口里

添加了安全区后,将其名称作为引用提供给管理接口。

例 3.9. 在管理接口里添加一个安全区

host=master/core-service=management/management-interface=http-interface/:write-attribute(name=security-realm,value=TestRealm)

3.8. 用于敏感字符串的密码库

3.8.1. 关于保护明码文件里的敏感字符

Web 应用程序和其他部署经常含有明码文件,如 XML 部署描述符,其中包含敏感信息如密码和其他敏感字符串。JBoss EAP 包含一个密码库(Password Vault)机制,可以让你加密敏感信息并将其存储在一个加密的密钥库(Keystore)里。这个库机制管理解密用于安全域、安全区或其他验证系统的字符串。这提供了一个额外的安全层。这个机制依赖于被支持的 JKD 实现里包含的一些工具。

3.8.2. 创建一个 Java 密钥库来存储敏感信息

前提条件

  • keytool 命令必须可用。它时 JRE 提供的命令。请找到这个文件所在的位置,在红帽企业版 Linux 里,它是 /usr/bin/keytool

过程 3.2. 设置 Java 密钥库

  1. 创建一个目录来存储你的密钥库和其他加密的信息。

    创建一个目录来存储你的密钥库和其他加密的信息。这个过程的剩余部分将假设这个目录是 /home/USER/vault/
  2. 确定 keytool 要使用的参数。

    确定下列参数:
    alias
    别名是 vault 或其他存储在密钥库里的数据的唯一标识符。这个过程结尾的命令示例里的别名是 vault。别名是区分大小写的。
    keyalg
    用于加密的算法。默认是 DSA。这个过程里的例子使用了 RSA。请查看你的 JRE 和操纵系统的文档,看那种选择是可用的。
    keysize
    加密密钥的大小影响了通过 brute force 解密的难度。默认的密钥大小是 1024。它必须是 512 到 1024 只见,且是 64 的倍数。这个过程里的例子使用了 1024
    keystore
    密钥库是一个保存加密信息以及如何解密的信息的数据库。如果你没有指定密钥库,默认的密钥库是你的主目录下的 .keystore。当第一次添加数据到密钥库时,它会被创建。这个过程里的例子使用了 vault.keystore 密钥库。
    keystore 命令有很多其他选项。更多的细节,请参考你的 JRE 或操纵系统的文档。
  3. 确定 keystore 命令会询问的问题的答案。

    keystore 需要下列信息来填充密钥库条目:
    密钥库密码
    当你创建一个密钥库时,你必须设置密码。为了和将来的密钥库一起使用,你需要提供密码。请选择一个你可以记住的强密码。密钥库的安全性取决于密码以及它所在的文件系统和操纵系统的安全性。
    密钥密码(可选)
    除了密钥库密码,你可以为每个保存的密钥指定一个密码。每次使用这个密钥时都需要输入密码。通常这个功能不会被使用。
    名和姓
    这和列表里余下的信息可以有助于唯一标识密钥并将其放入一个其他密钥的层次结构里。它完全可以不是一个名字,但应该是两个单词,而且是唯一的。这个过程里的例子使用了 Accounting Administrator。按照目录的术语,它成为了证书的通用名称(common name)
    机构内部门(Organizational unit)
    这是一个标识谁在使用证书的单词。它可以是应用程序或商业单元。这个过程里的例子使用了AccountingServices。通常,组或应用程序使用的所有密钥库都使用相同的机构内部门。
    机构
    这通常是一个代表你的机构名称的单词。它通常在机构使用的所有证书里都保持相同。这个例子使用了MyOrganization
    城市或自治区
    你的城市
    州或省
    你的州或省,或者相等的地区
    国家
    两个字母的国家代码
    所有这些信息将创建一个密钥库和证书的层次结构,确保它们使用一致而唯一的命名结构。
  4. 运行 keytool 命令,提供你收集的信息。

    例 3.10. keystore 命令的输入和输出的例子。

    $ keytool -genkey -alias vault -keyalg RSA -keysize 1024 -keystore /home/USER/vault/vault.keystore
    Enter keystore password: vault22 
    Re-enter new password:vault22 
    What is your first and last name?
      [Unknown]:  Accounting Administrator
    What is the name of your organizational unit?
      [Unknown]:  AccountingServices
    What is the name of your organization?
      [Unknown]:  MyOrganization
    What is the name of your City or Locality?
      [Unknown]:  Raleigh
    What is the name of your State or Province?
      [Unknown]:  NC
    What is the two-letter country code for this unit?
      [Unknown]:  US
    Is CN=Accounting Administrator, OU=AccountingServices, O=MyOrganization, L=Raleigh, ST=NC, C=US correct?
      [no]:  yes
    
    Enter key password for <vault>
            (RETURN if same as keystore password):
    
结果

/home/USER/vault/ 目录里创建了一个名为 vault.keystore 的文件。它保存了一个名为 vault 的密钥,这个密钥将为用来为 JBoss EAP 保存加密字符串,如密码。

3.8.3. 设置密钥库密码的掩码并初始化密码库

前提条件

  1. 运行 vault.sh 命令。

    运行 EAP_HOME/bin/vault.sh。输入 0 启动新的交互式会话。
  2. 输入保存加密文件的目录。

    这个目录应该比较安全,但 JBoss EAP 需要能够访问它。如果你按照 第 3.8.2 节 “创建一个 Java 密钥库来存储敏感信息” 进行,你的密钥库应该位于主目录的 vault/ 里。这个例子使用了目录 /home/USER/vault/

    注意

    不要忘记目录名后面的斜杠。根据操作系统来使用 /\
  3. 输入密钥库的路径。

    输入密钥库文件的完整路径。这个例子使用了 /home/USER/vault/vault.keystore
  4. 加密密钥库的密码。

    下面的步骤加密了密钥库的密码,所以你可以在配置文件和应用程序里安全地使用它了。
    1. 输入密钥库的密码。

      遇到提示时,输入密钥库的密码。
    2. 输入一个 salt 值。

      输入 8 个字符的 salt 值。这个 salt 值和下面的迭代计数用于创建哈希值。
    3. 输入迭代计数。

      输入迭代计数的值。
    4. 记录密码掩码信息。

      密码掩码、salt 和迭代计数都输出在标准输出里。将其记录在一个安全的位置。攻击者可能用它们来破解密码。
    5. 输入 vault 的别名。

      遇到提示时,输入 vault 的别名。如果你按照 第 3.8.2 节 “创建一个 Java 密钥库来存储敏感信息” 来创建 vault,那么别名应该是 vault
  5. 退出交互式控制台。

    输入 exit 来退出交互式控制台。
结果

你的密钥库密码已设置掩码,可用于配置文件和部署了。此外,你的 vault 已配置完全且可以使用了。

3.8.4. 配置 JBoss EAP 来使用密码库

概述

在你可以在配置文件里为密码设置掩码和其他敏感属性之前,你需要让 JBoss EAP 知晓存储和破解密码的密码库。请按照下列步骤来启用这个功能。

过程 3.3. 设置密码库

  1. 确定这个命令的正确的值。

    确定下列参数的值,这个命令将用它们来创建密钥库。关于创建密钥库的信息,请参考下列主题:第 3.8.2 节 “创建一个 Java 密钥库来存储敏感信息”第 3.8.3 节 “设置密钥库密码的掩码并初始化密码库”
    参数 描述
    KEYSTORE_URL
    密钥库文件的 URL 的文件系统路径,它通常类似于 vault.keystore
    KEYSTORE_PASSWORD
    用来访问密钥库的密码。这个值应该被标记。
    KEYSTORE_ALIAS
    密钥库的名称。
    SALT
    用来加密和解密密钥库值的 Salt。
    ITERATION_COUNT
    运行加密算法的次数。
    ENC_FILE_DIR
    密钥库命令运行的目录路径。通常是包含密码库的目录。
    host (managed domain only)
    你在配置的主机的名称
  2. 使用管理 CLI 来启用密码库。

    根据使用的是受管域还是独立服务器配置,分别运行下列命令。用这个步骤里第一步里的值替换命令里的值。
    • 受管域

      /host=YOUR_HOST/core-service=vault:add(vault-options=[("KEYSTORE_URL" => "PATH_TO_KEYSTORE"), ("KEYSTORE_PASSWORD" => "MASKED_PASSWORD"), ("KEYSTORE_ALIAS" => "ALIAS"), ("SALT" => "SALT"),("ITERATION_COUNT" => "ITERATION_COUNT"), ("ENC_FILE_DIR" => "ENC_FILE_DIR")])
      
    • 独立服务器

      /core-service=vault:add(vault-options=[("KEYSTORE_URL" => "PATH_TO_KEYSTORE"), ("KEYSTORE_PASSWORD" => "MASKED_PASSWORD"), ("KEYSTORE_ALIAS" => "ALIAS"), ("SALT" => "SALT"),("ITERATION_COUNT" => "ITERATION_COUNT"), ("ENC_FILE_DIR" => "ENC_FILE_DIR")])
      
    下面是带有假定值的命令示例:
    /core-service=vault:add(vault-options=[("KEYSTORE_URL" => "/home/user/vault/vault.keystore"), ("KEYSTORE_PASSWORD" => "MASK-3y28rCZlcKR"), ("KEYSTORE_ALIAS" => "vault"), ("SALT" => "12438567"),("ITERATION_COUNT" => "50"), ("ENC_FILE_DIR" => "/home/user/vault/")])
    
结果

JBoss EAP 已配置好通过密码库来破解掩码字符串。要添加字符串到库里并在配置里使用它们,请参考下列主题:第 3.8.5 节 “在 Java 密钥库里保存和获取加密的敏感字符串”

3.8.5. 在 Java 密钥库里保存和获取加密的敏感字符串

总结

在普通文本配置文件里包含密码和其他敏感字符串是不安全的。JBoss EAP 包括了在加密的密钥库里存储并 mask 这些敏感字符串,以及在配置文件里使用 mask 值。

过程 3.4. 设置 Java 密钥库

  1. 运行 vault.sh 命令。

    运行 EAP_HOME/bin/vault.sh。输入 0 启动新的交互式会话。
  2. 输入保存加密文件的目录。

    如果你遵循 第 3.8.2 节 “创建一个 Java 密钥库来存储敏感信息”,你的密钥库将位于主目录里的 vault/ 下。在多数情况下,将所有加密信息保存在相同的位置是有意义的。这个例子使用了 /home/USER/vault/

    注意

    不要忘记目录名后面的斜杠。根据操作系统来使用 /\
  3. 输入密钥库的路径。

    输入密钥库文件的完整路径。这个例子使用了 /home/USER/vault/vault.keystore
  4. 输入密钥库密码、Vault 名、Sale 和迭代计数。

    遇到提示时,输入密钥库密码、Vault 名、Sale 和迭代计数。然后将执行握手操作。
  5. 选择保存密码的选项。

    选择选项 0 来保存密码或其他敏感字符串。
  6. 输入这个值。

    遇提示时,将这个值输入两次。如果两次的值不匹配,会提示你再次输入。
  7. 输入 Vault Block。

    输入 Vault Block,
  8. 输入属性名称。

    输入你在保存的属性的名称。一个例子就是 password
    结果

    类似于下面的信息表示这个属性已经被保存了。

    Attribute Value for (ds_ExampleDS, password) saved
  9. 记录关于加密字符串的信息。

    标准输出的信息将显示 vault block、属性名称、共享密钥和在配置里使用字符串的建议。请在安全的地方记录这些信息。下面是输出示例。
    ********************************************
    Vault Block:ds_ExampleDS
    Attribute Name:password
    Shared Key:N2NhZDYzOTMtNWE0OS00ZGQ0LWE4MmEtMWNlMDMyNDdmNmI2TElORV9CUkVBS3ZhdWx0
    Configuration should be done as follows:
    VAULT::ds_ExampleDS::password::N2NhZDYzOTMtNWE0OS00ZGQ0LWE4MmEtMWNlMDMyNDdmNmI2TElORV9CUkVBS3ZhdWx0
    ********************************************
    
  10. 在你的配置里使用加密的字符串。

    在你的配置里使用前面步骤里的字符串来替代普通文本字符串。下面是使用加密密码的数据源。
    ...
      <subsystem xmlns="urn:jboss:domain:datasources:1.0">
        <datasources>
          <datasource jndi-name="java:jboss/datasources/ExampleDS" enabled="true" use-java-context="true" pool-name="H2DS">
            <connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1</connection-url>
            <driver>h2</driver>
            <pool></pool>
            <security>
              <user-name>sa</user-name>
              <password>${VAULT::ds_ExampleDS::password::N2NhZDYzOTMtNWE0OS00ZGQ0LWE4MmEtMWNlMDMyNDdmNmI2TElORV9CUkVBS3ZhdWx0}</password>
            </security>
          </datasource>
          <drivers>
             <driver name="h2" module="com.h2database.h2">
                <xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
             </driver>
          </drivers>
        </datasources>
      </subsystem>
    ...
    
    
    你可以在允许表达式的任何域或独立配置文件里使用加密字符串。

    注意

    要检查某个子系统里是否允许表达式,请在这个子系统里运行下列 CLI 命令:
    /host=master/core-service=management/security-realm=TestRealm:read-resource-description(recursive=true)
    在这个命令的输出里,查找 expressions-allowed 参数的值。如果它为 true,你可以在这个特定子系统的配置里使用表达式。
    在密钥库里保存了字符串后,请使用下列语法用加密字符串替代任何明文字符串。
    ${VAULT::<replaceable>VAULT_BLOCK</replaceable>::<replaceable>ATTRIBUTE_NAME</replaceable>::<replaceable>ENCRYPTED_VALUE</replaceable>}
    
    下面是一个例子,其 Vault Block 是 ds_ExampleDS,而属性是 password
    <password>${VAULT::ds_ExampleDS::password::N2NhZDYzOTMtNWE0OS00ZGQ0LWE4MmEtMWNlMDMyNDdmNmI2TElORV9CUkVBS3ZhdWx0}</password>
    

3.8.6. 存储和解析应用程序里的敏感字符串

概述

JBoss EAP 的 Configuration 元素支持使用安全库(Security Vault)机制根据 Java 密钥库里保存的值来解析加密的字符串。你可以在自己的应用程序里添加对这个功能的支持。

首先,添加这个密码到库里。然后,用保存在库的密码替换明文密码。你可以使用这个方法来隐藏应用程序里的任何敏感字符串。
前提条件

在执行这个过程之前,请确保保存库文件的目录是存在的。将其保存在什么位置是无所谓的,只要执行 JBoss EAP 的用户有读写这些文件的权限。这个例子将 vault/ 目录放到 /home/USER/vault/ 目录里。这个库本身也是 vault/ 里一个名为 vault.keystore 的文件。

例 3.11. 添加密码字符串到库里

请用 EAP_HOME/bin/vault.sh 命令添加这个字符串到库里。下面的屏幕输出包含了这个命令的完整过程和结果。用户输入的值被高亮显示。出于格式的考虑,我们删除了一些输出。在 Microsoft Windows 里,这个命令是 vault.bat。请注意,在 Microsoft Windows 里,文件路径要使用 \ 来分隔,而不是 /
[user@host bin]$ ./vault.sh 
**********************************
****  JBoss Vault ********
**********************************
Please enter a Digit::   0: Start Interactive Session  1: Remove Interactive Session  2: Exit
0
Starting an interactive session
Enter directory to store encrypted files:/home/user/vault/
Enter Keystore URL:/home/user/vault/vault.keystore
Enter Keystore password: ...
Enter Keystore password again: ...
Values match
Enter 8 character salt:12345678
Enter iteration count as a number (Eg: 44):25

Enter Keystore Alias:vault
Vault is initialized and ready for use
Handshake with Vault complete
Please enter a Digit::   0: Store a password  1: Check whether password exists  2: Exit
0
Task:  Store a password
Please enter attribute value: sa
Please enter attribute value again: sa
Values match
Enter Vault Block:DS
Enter Attribute Name:thePass
Attribute Value for (DS, thePass) saved

Please make note of the following:
********************************************
Vault Block:DS
Attribute Name:thePass
Shared Key:OWY5M2I5NzctYzdkOS00MmZhLWExZGYtNjczM2U5ZGUyOWIxTElORV9CUkVBS3ZhdWx0
Configuration should be done as follows:
VAULT::DS::thePass::OWY5M2I5NzctYzdkOS00MmZhLWExZGYtNjczM2U5ZGUyOWIxTElORV9CUkVBS3ZhdWx0
********************************************

Please enter a Digit::   0: Store a password  1: Check whether password exists  2: Exit
2
将被加入到 Java 代码里的字符串是输出结果里最后面的值,就是以 VAULT 开始的行。
下列 servlet 使用了 vaulted 字符串而不是明文密码。明文版本被注释了,你可以看到其中的区别。

例 3.12. 使用 vaulted 密码的 servlet

package vaulterror.web;
 
import java.io.IOException;
import java.io.Writer;
 
import javax.annotation.Resource;
import javax.annotation.sql.DataSourceDefinition;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
 
 
/*@DataSourceDefinition(
        name = "java:jboss/datasources/LoginDS",
        user = "sa",
        password = "sa",
        className = "org.h2.jdbcx.JdbcDataSource",
        url = "jdbc:h2:tcp://localhost/mem:test"
)*/
@DataSourceDefinition(
        name = "java:jboss/datasources/LoginDS",
        user = "sa",
        password = "VAULT::DS::thePass::OWY5M2I5NzctYzdkOS00MmZhLWExZGYtNjczM2U5ZGUyOWIxTElORV9CUkVBS3ZhdWx0",
        className = "org.h2.jdbcx.JdbcDataSource",
        url = "jdbc:h2:tcp://localhost/mem:test"
)
@WebServlet(name = "MyTestServlet", urlPatterns = { "/my/" }, loadOnStartup = 1)
public class MyTestServlet  extends HttpServlet {
 
    private static final long serialVersionUID = 1L;
 
 
    @Resource(lookup = "java:jboss/datasources/LoginDS")
    private DataSource ds;
 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Writer writer = resp.getWriter();
        writer.write((ds != null) + "");
    }
}
你的 servlet 现在可以解析 vaulted 字符串了。

3.9. LDAP

3.9.1. 关于 LDAP

轻量级目录访问协议(Lightweight Directory Access Protocol,LDAP)是一个在网络里存储和分发目录信息的协议。这个目录信息包含关于用户、硬件设备、访问角色和限制以及其他信息。
LDAP 的一些常见实现包括 OpenLDAP、Microsoft Active Directory、IBM Tivoli Directory Server、Oracle Internet Directory 等。
JBoss EAP 带有几个验证和授权模块,它们允许在 Web 和 EJB 应用程序里将 LDAP 服务器用于验证和授权。

3.9.2. 在管理接口里使用 LDAP 进行验证

要在管理控制台、管理 CLI 或 API 里将 LDAP 目录服务器用作验证源,你需要执行下列过程:
  1. 创建一个到 LDAP 服务器的转出连接。
  2. 创建一个启用 LDAP 的安全区。
  3. 在管理接口里引用新的安全区。
创建一个到 LDAP 服务器的转出连接

LDAP 转出连接允许下列属性:

表 3.1. LDAP 转出连接的属性

属性 必需的 描述
名称
标识这个连接的名称。这个名称用在安全区定义里。
url
目录服务器的 URL 地址。
search-dn
授权执行搜索的用户的全限定可区分名称(Distinguished Name,DN)。
search-credentials
用户授权执行搜索的密码。
initial-context-factory
当建立连接时使用的初始上下文。默认为 com.sun.jndi.ldap.LdapCtxFactory

例 3.13. 添加 LDAP 转出连接

这个例子用下列属性集添加了一个转出连接:
  • 搜索 DN: cn=search,dc=acme,dc=com
  • 搜索凭证: myPass
  • URL: ldap://127.0.0.1:389
/host=master/core-service=management/ldap-connection=ldap_connection/:add(search-credential=myPass,url=ldap://127.0.0.1:389,search-dn="cn=search,dc=acme,dc=com")

例 3.14. 代表 LDAP 转出连接的 XML 片段

<outbound-connections>
   <ldap name="ldap_connection" url="ldap://127.0.0.1:389" search-dn="cn=search,dc=acme,dc=com" search-credential="myPass" />
</outboundconnections>	
	

创建一个启用 LDAP 的安全区

管理接口可以针对 LDAP 服务器而不是默认的基于属性文件的安全区进行验证。LDAP 验证器将首先建立一个和远程目录服务器的连接,然后使用传入验证系统的用户名来执行搜索以找到 LDAP 记录的全限定可区分名称(Distinguished Name,DN)。新的连接将以用户的 DN 为凭证以及用户提供的密码来建立。如果这个针对 LDAP 服务器的验证成功,DN 就被证明为有效的。

LDAP 安全区需要下列配置属性和元素来执行它的功能。
connection
<outbound-connections> 里定义的连接的名称,用来连接 LDAP 目录。
base-dn
用户开始搜索的上下文的可区分的名称。
recursive
搜索是否应该在 LDAP 目录树里进行递归,或者只搜索指定的上下文。默认为 false
user-dn
保存可区分名称的用户的属性。它会被用来测试验证。默认为 dn
子元素是 username-filteradvanced-filter 中的一个。
username-filter 采用一个名为 attribute 的属性,它的值是保存用户名的 LDAP 属性的名称,如 userNamesambaAccountName
advanced-filter 采用一个名为 filter 的属性。这个属性包含以标准 LDAP 语法编写的过滤器队列。请小心地将 & 字符修改为 &amp;。下面是一个过滤器的例子:
(&(sAMAccountName={0})(memberOf=cn=admin,cn=users,dc=acme,dc=com))
After escaping the ampersand character, the filter appears as:
(&amp;(sAMAccountName={0})(memberOf=cn=admin,cn=users,dc=acme,dc=com))

例 3.15. 代表启用了 LDAP 的安全区的 XML 片段

这个例子使用了下列参数:
  • connection - ldap_connection
  • base-dn - cn=users,dc=acme,dc=com.
  • username-filter - attribute="sambaAccountName"
<security-realm name="TestRealm">
   <authentication>
      <ldap connection="ldap_connection" base-dn="cn=users,dc=acme,dc=com">
         <username-filter attribute="sambaAccountName" />
      </ldap>
  </authentication>
</security-realm>	


警告

确保不允许空 LDAP 密码是很重要的;除非你故意这么做,但这是严重的安全隐患。
EAP 6.1 包含一个用于 CVE-2012-5629 的补丁,它设置 LDAP 登录模块的 allowEmptyPasswords 选项为 false(如果它还没有被设置)。在旧的版本里,这个选项应该手动进行配置。

例 3.16. 添加 LDAP 安全区

下面的命令添加了一个安全区并针对独立服务器设置其属性。
/host=master/core-service=management/security-realm=ldap_security_realm/authentication=ldap:add(base-dn="DC=mycompany,DC=org", recursive=true, username-attribute="MyAccountName", connection="ldap_connection")
应用新的安全区到管理接口里

在创建了安全区后,你需要在管理接口的配置里引用它。管理接口将使用安全区来进行 HTTP digest 验证。

例 3.17. 应用安全区到 HTTP 接口里

在配置完成后,你重启主机控制器,基于 web 的管理控制台将使用 LDAP 来验证用户。
/host=master/core-service=management/management-interface=http-interface/:write-attribute(name=security-realm,value=TestRealm)

第 4 章 Java 安全性管理者

4.1. 关于 Java 安全性管理者

Java 安全性管理者(Java Security Manager)
Java 安全性管理者是一个管理 Java 虚拟机(JVM)sandbox 外部边界的类,它控制代码在 JVM 里执行时如何和外部的资源交互。当 Java 安全性管理者被激活时,Java API 在执行许多有潜在风险的操作之前会检查 Java 安全性管理者以获得批准。
Java 安全性管理者使用一个安全策略来确定是否允许或拒绝给定的动作。

4.2. 关于 Java 安全管理者策略

安全策略
以不同代码类别表示的一系列权限。Java 安全管理者将应用程序的动作请求和安全策略进行比较。如果某个动作是策略所允许的,那么安全管理者将允许这个动作执行。如果这个动作是策略所不允许的,那么将拒绝这个动作的执行。安全策略可以根据代码的位置或签名来定义权限。
Java 安全管理者和所用的安全策略是用虚拟机选项 java.security.managerjava.security.policy 配置的。

4.3. 在 Java 安全管理者里运行 JBoss EAP

要指定一个 Java 安全管理者策略,你需要编辑在引导过程中传递给域或服务器实例的 Java 选项。出于这个原因,你不能将参数作为选项传递给 domain.shstandalone.sh 脚本。下面的过程会指引你配置实例以在 Java 安全管理者里运行 JBoss EAP。

前提条件

  • 在执行下列过程之前,你需要用 JDK 里包含的 policytool 编写一个安全策略。这个过程假定你的策略位于 EAP_HOME/bin/server.policy
  • 在编辑配置文件之前,域或独立服务器必须完全停止。
如果你有域成员跨越多个系统,请对每个域里的物理主机或实例执行下列过程。

过程 4.1. 编辑配置文件

  1. 打开配置文件。

    打开配置文件进行编辑。根据你使用的是域还是独立服务器,这个文件位于两个位置中的一个。它不是用来启动服务器或域的可执行文件。
    • 受管域

      EAP_HOME/bin/domain.conf
    • 独立服务器

      EAP_HOME/bin/standalone.conf
  2. 在文件末尾添加 Java 选项。

    在文件结尾添加下列行。你可以修改 -Djava.security.policy 值来制定安全策略的准确位置。这个值应该只有一行,且不能中断。你也可以修改 -Djava.security.debug、指定调试级别来记录更多或更少的信息。完整的选项是 failure,access,policy
    JAVA_OPTS="$JAVA_OPTS -Djava.security.manager -Djboss.home.dir=$PWD/.. -Djava.security.policy==$PWD/server.policy -Djava.security.debug=failure"
    
    
  3. 启动域或服务器。

    正常地启动域或服务器。

4.4. 编写 Java 安全性管理者策略

简介

多数 JDK 和 JRE 版本里都包含一个 policytool 程序,它用于创建和编辑 Java 安全管理者安全策略。关于 policytool 的详细信息请访问 http://docs.oracle.com/javase/6/docs/technotes/tools/

基本信息

安全策略由下列配置元素组成:

CodeBase
产生代码的 URL 位置(不包括主机和域信息)。这个参数是可选的。
SignedBy
密钥库里用来引用签名者(其私有密钥用于为代码签名)的别名。这可以是单个的值,也可以是用逗号隔开的值的列表。如果忽略它,签名的缺席与否都不会影响 Java 安全管理者。
Principals
principal_type/principal_name 对的列表,它必须出现在执行线程的 principal 集里。Principal 条目是可选的。如果忽略它,则表示 “任何 principals“。
权限
权限是赋予代码的访问许可。许多权限是作为 Java EE 5 规格的一部分提供的。这个文档只涵盖了 JBoss EAP 提供的额外的权限。

过程 4.2. 设置新的 Java 安全性管理者策略

  1. 启动 policytool.

    以下列方式之一启动 policytool 工具。
    • 红帽企业版 Linux

      在 GUI 或命令行提示下,运行 /usr/bin/policytool
    • Microsoft Windows Server

      从开始菜单或 Java 安装的 bin\ 里运行 policytool.exe。在不同系统里,其位置可能会不一样。
  2. 创建一个新的策略。

    要创建新的策略,请选择 Add Policy Entry。添加你需要的参数,然后点击 Done
  3. 编辑现有的策略

    从现有的策略列表里选择策略,并选择 Edit Policy Entry 按钮。然后根据需要编辑相关参数。
  4. 删除现有的策略。

    从现有的策略列表里选择策略,并选择 Delete Policy Entry 按钮。

JBoss EAP 专有的权限

org.jboss.security.SecurityAssociation.getPrincipalInfo
提供对 org.jboss.security.SecurityAssociationgetPrincipal()getCredential() 方法的访问。使用这个运行时权限涉及的风险是可以看到当前的线程调用者和凭证。
org.jboss.security.SecurityAssociation.getSubject
提供对 org.jboss.security.SecurityAssociationgetSubject() 方法的访问。
org.jboss.security.SecurityAssociation.setPrincipalInfo
提供对 org.jboss.security.SecurityAssociationsetPrincipal(), setCredential(), setSubject(), pushSubjectContext()popSubjectContext() 方法的访问。
org.jboss.security.SecurityAssociation.setServer
提供对 org.jboss.security.SecurityAssociationsetServer 方法的访问。使用这个运行时权限涉及的风险是可以启用或禁用调用 principal 和凭证的多线程存储。
org.jboss.security.SecurityAssociation.setRunAsRole
提供对 org.jboss.security.SecurityAssociationpushRunAsRole, popRunAsRole, pushRunAsIdentitypopRunAsIdentity 方法的访问。使用这个运行时权限涉及的风险是可以修改当前调用者的 run-as 角色 principal。
org.jboss.security.SecurityAssociation.accessContextInfo
提供对 org.jboss.security.SecurityAssociationaccessContextInfoaccessContextInfo 的 getter 和 setter 方法的访问。这允许你设置和获取当前安全上下文信息。
org.jboss.naming.JndiPermission
提供对指定 JNDI 树路径里的文件和目录或递归至全部文件和子目录的特殊权限。JndiPermission 由一个路径名和相对于文件或目录的一系列有效权限组成。
可用的权限包括:
  • bind
  • rebind
  • unbind
  • lookup
  • list
  • listBindings
  • createSubcontext
  • all
Pathnames ending in /* indicate that the specified permissions apply to all files and directories of the pathname. Pathnames ending in /- indicate recursive permissions to all files and subdirectories of the pathname. Pathnames consisting of the special token <<ALL BINDINGS>> matches any file in any directory.
org.jboss.security.srp.SRPPermission
保护对敏感 SRP 信息(如私有会话密钥和私有密钥)访问的自定义权限类。这个权限没有定义任何动作。getSessionKey 目标提供对 SRP 协商导致的私有会话密钥的访问。对这个密钥的访问允许你加密和解密已经用这个会话密钥加密的消息。
org.hibernate.secure.HibernatePermission
这个权限类提供对于安全 Hibernate 会话的基本权限。这个属性的目标是实体名称。可用的动作包括:
  • insert
  • delete
  • update
  • read
  • * (all)
org.jboss.metadata.spi.stack.MetaDataStackPermission
提供一个自定义权限类来控制调用者如何和元数据栈进行交互。可用的权限是:
  • modify
  • push (onto the stack)
  • pop (off the stack)
  • peek (onto the stack)
  • * (all)
org.jboss.config.spi.ConfigurationPermission
配置属性的安全设置。只定义权限目标名称,不定义动作。这个属性的目标包括:
  • <property name> (the property this code has permission to set)
  • * (all properties)
org.jboss.kernel.KernelPermission
对于内核配置的安全访问。只定义权限目标,不定义动作。这个属性的目标包括:
  • access (to the kernel configuration)
  • configure (implies access)
  • * (all)
org.jboss.kernel.plugins.util.KernelLocatorPermission
对于内核的安全访问。只定义权限目标,不定义动作。这个属性的目标包括:
  • kernel
  • * (all)

4.5. 调试安全管理者策略

你可以启用调试信息来帮助你解决和安全策略相关的问题。java.security.debug 选项配置和安全相关的信息的级别。java -Djava.security.debug=help 命令将产生具有完整调试选项的帮助信息。当解决和安全相关的故障而完全不知道原因时,设置调试级别为 all 是很有用的。但对于普通的用途而言,这会产生过多的信息。一般默认的选项是 access:failure

过程 4.3. 启用普通调试

  • 这个过程将启用普通级别的和安全相关的调试信息。

    在服务器配置文件里添加下列行。
    • 如果 JBoss EAP 实例运行在受管域里,这一行将添加到 bin/domain.conf()或 bin/domain.conf.bat(Windows)。
    • 如果 JBoss EAP 实例作为独立服务器运行,这一行将添加到 bin/standalone.conf()或 bin\standalone.conf.bat(Windows)。
Linux
JAVA_OPTS="$JAVA_OPTS -Djava.security.debug=access:failure"
Windows
JAVA_OPTS="%JAVA_OPTS% -Djava.security.debug=access:failure"
结果

启用了普通级别的和安全相关的调试信息。

第 5 章 安装补丁

5.1. 关于补丁机制

JBoss 安全和程序错误补丁以两种方式发布。
  • 计划中的更新:作为现有产品的小版本、次要版本、主要版本升级的一部分。
  • 异步更新:作为一次性的补丁,它在现有产品的常规更新周期之外发布。
决定是否以计划中更新或周期之外的一次性更新发布补丁取决于要修复的漏洞的严重性。影响低的漏洞通常会推迟到下一个次要版本里解决。而中等或影响更严重的漏洞则通常按照严重性在只包含相应解决方法的异步版本里发布。
安全漏洞的严重性由红帽的安全响应团队对程序错误进行评估,综合下列事实:
  • 发现漏洞的容易程度?
  • 如果出现漏洞,会造成什么样的损害?
  • 有无其他降低漏洞影响的因素(如防火墙、Security-Enhanced Linux 、编译器指令等)?
红帽还维护了一个通知订阅者安全漏洞的电子邮件列表。 第 5.2 节 “订阅补丁邮件列表(Patch Mailing List)”
关于红帽对 JBoss 安全漏洞评级的更多信息,请点击:http://securityblog.redhat.com/2012/09/19/how-red-hat-rates-jboss-security-flaws/

5.2. 订阅补丁邮件列表(Patch Mailing List)

总结

红帽的 JBoss 团队维护了一个用于中间件安全通知的邮件列表。本节涵盖订阅这个列表所需的步骤。

前提条件

过程 5.1. 订阅 JBoss Watch List

  1. 点击下列链接进入 JBoss Watch 邮件列表页面:JBoss Watch Mailing List
  2. Subscribing to Jboss-watch-list 部分输入你的电子邮件地址。
  3. [你可能也想输入你的名字和密码。这完全由你决定,但我们不推荐这么做。]
  4. 点击 Subscribe 按钮启动订阅过程。
  5. 你可以访问 JBoss Watch Mailing List Archives 来浏览邮件列表的归档。
结果

在确认你的电子邮件账号之后,你将订阅 JBoss 补丁邮件列表来接收安全相关的通知。

5.3. 以 ZIP 形式安装补丁

总结

JBoss 安全补丁以两种形式发布:ZIP(用于所有产品)和 RPM(用于产品的子集)。JBoss 的程序错误修复补丁只以 ZIP 形式发布。本节描述了适用 ZIP 形式安装补丁(安全或程序错误修复)的步骤。

前提条件

  • 对红帽客户门户的有效访问和订阅。
  • 对以 ZIP 形式安装的 JBoss 产品的当前订阅。

过程 5.2. 通过 ZIP 方式对 JBoss 产品应用补丁

JBoss 产品的安全更新是由勘误(ZIP 和 RPM 方法皆可)提供的。勘误封装了要解决的漏洞的列表、严重性级别、受影响的产品、漏洞的文本描述、对补丁的引用。程序错误修复的更新是不通过勘误来通知的。
对于 JBoss 产品的 ZIP 发行版本,勘误包含了一个客户门户上可以下载补丁 ZIP 文件的 URL 链接。这个下载文件包含了现有 JBoss 产品的补丁版本且只包含自前一次安装后已修改的文件。

警告

在安装补丁以前,你必需备份你的 JBoss 产品以及所有自定义的配置文件。
  1. 通过订阅 JBoss Watch 邮件列表或浏览 JBoss Watch 邮件列表归档来获得安全补丁的通知。

    注意

    JBoss Watch 邮件列表只通知安全补丁。
  2. 阅读安全补丁的勘误并确认它可以应用于你的 JBoss 产品。
  3. 如果安全补丁适用于你的 JBoss 产品,请用这个链接从红帽客户门户下载补丁文件。
  4. 从客户门户下载的 ZIP 文件会包含修复安全问题或程序错误所需的所有文件。下载这个补丁 ZIP 文件到和 JBoss 产品相同的位置。
  5. 在安装 JBoss 产品的位置解压补丁文件,覆盖现有的版本。
结果

JBoss 产品用 ZIP 文件方式更新了最新的补丁。

5.4. 以 RPM 形式安装补丁

总结

JBoss 安全补丁以两种形式发布:ZIP(用于所有产品)和 RPM(用于产品的子集)。JBoss 的程序错误修复补丁只以 ZIP 形式发布。本节描述了适用 RPM 形式安装补丁的步骤。RPM 更新方法只用来发布安全性异步补丁和小/次要/主要版本的产品更新。

前提条件

  • 对红帽网络的有效订阅。
  • 对以 RPM 形式安装的 JBoss 产品的当前订阅。

过程 5.3. 通过 RPM 方式对 JBoss 产品应用补丁

JBoss 产品的安全更新是由勘误(ZIP 和 RPM 方法皆可)提供的。勘误封装了要解决的漏洞的列表、严重性级别、受影响的产品、漏洞的文本描述、对补丁的引用。
对于 JBoss 产品的 RPM 发行版本,勘误包含了对要更新的 RPM 软件包的引用。这个补丁可以通过 yum 或其他 PRM 工具来安装。

警告

在安装补丁以前,你必需备份你的 JBoss 产品以及所有自定义的配置文件。
  1. 通过订阅 JBoss Watch 邮件列表或浏览 JBoss Watch 邮件列表归档来获得安全补丁的通知。
  2. 阅读安全补丁的勘误并确认它可以应用于你的 JBoss 产品。
  3. 如果安全补丁适用于你的 JBoss 产品,请用这个链接从红帽客户门户下载包含在勘误里的 PRM 软件包。
  4. 请使用
    yum update
    或类似的命令来安装补丁。
结果

JBoss 产品用 RPM 软件包方式更新了最新的补丁。

5.5. JBoss 安全补丁的严重性和影响级别

为了评估每个 JBoss 安全性漏洞的风险,红帽使用四个安全级别,分别为低(Low)、中等(Moderate)、重要(Important)、严重(Critical),以及通用缺陷评估系统(Common Vulnerability Scoring System,CVSS)v2 来确定漏洞产生的影响。

表 5.1. JBoss 安全补丁的严重性级别

严重性 描述
严重(Critical)
这个级别是容易被未授权的远程攻击者利用的漏洞,它可能导致系统破解(任意代码执行)而无需用户干预。这些漏洞易受蠕虫病毒利用。要求经验证的远程用户、本地用户或不太可能的配置的漏洞不会被归类于严重(Critical)影响级别。
重要(Important)
这个级别的漏洞可以轻易地破解资源的机密性、完整性或能力。这些漏洞允许本地用户获取权限,允许未验证的远程用户查看本应该被验证保护的资源、执行任意的代码,或者允许本地或远程用户导致服务拒绝问题。
中等(Moderate)
这个级别的漏洞可能更难被利用,但仍然会导致资源的机密性、完整性或能力某种程度的破解。这些漏洞可能产生严重或重要的影响,但根据技术评估,会较不容易被利用或影响不太可能的配置。
低(Low)
其他具有安全性影响的漏洞都归类于这个级别。这些漏洞在不太可能的情况下才会被利用,或者即使被利用也只会造成最小的后果。
CVSS v2 分数的影响构成基于对三个潜在影响的联合评估:Confidentiality (C)、Integrity (I) 和 Availability (A)。每种影响都评级为:None (N)、Partial (P) 或 Complete (C)。
因为 JBoss 服务器进程是以无特权的用户运行的,且和主机操作系统是隔离的,JBoss 安全漏洞只会被评级为 None(N) 或 Partial (P)。

例 5.1. CVSS v2 影响分数

下面的例子展示了一个 CVSS v2 影响分数,对漏洞的利用不会影响系统的保密性(confidentiality),部分影响系统完整性(integrity),而完全影响系统的可用性(availability)(也就是说,系统将完全无法使用,例如内核崩溃)。
C:N/I:P/A:C
综合安全性评级和 CVSS 分数,机构可以根据漏洞对每个环境产生的风险来作出决定,并相应地安排系统升级。
关于 CVSS2 的更多信息,请参考:CVSS2 Guide

第 6 章 安全域

6.1. 关于安全域

安全域是 JBoss EAP 安全子系统的一部分。所有的安全配置现在都由受管域的域控制器或独立服务器来集中管理了。
安全域由验证、授权、安全映射和审计的配置组成。它实现了 Java 验证和授权服务(Java Authentication and Authorization Service,JAAS) 声明式安全性。
验证指的是检验用户的身份。按照安全性术语,这个用户被称为 principal。虽然验证的授权是不同的,系统包含的许多验证模块也可以处理授权。
授权是一个安全策略,它包含允许或禁止某些行为的信息。按照安全性术语,它也常被称为角色。
安全映射(Security mapping)指的是将信息传递给应用程序之前在 principal、角色或属性里添加、修改、删除信息的能力。。
审计管理者允许你配置提供者模块(provider modules)来控制报告安全事件的方式。
如果你使用安全域,你可以从应用程序删除所有的专有安全配置。这允许你集中地修改安全参数。受益于这种配置结构的一个常见场景是在测试和产品环境间移动应用程序。

6.2. 关于 Picketbox

Picketbox 是基础的安全框架,它为运行在 JBoss EAP 里的 Java 应用程序提供了验证、授权、审计和映射能力。它用单一的配置在单一的框架里提供了这些能力:

6.3. 关于验证

验证指的是确定一个主题并检验标识的真实性。最常见的验证机制是用户名和密码的组合。其他常见的机制使用共享密钥、智能卡或指纹。按照 Java EE 的声明安全性,成功验证的结果被称为 principal。
JBoss EAP 使用一个可插拔的验证模块系统,它提供灵活性以及与机构里已使用的验证系统的集成性。每个安全域都包含一个或多个配置好的验证模块。每个模块都包含其他的配置参数来自定义其行为。配置验证子系统的最简单的方法是通过基于 web 的管理控制台。
验证和授权不同,虽然他们经常联系在一起。许多包含的验证模块也可以处理授权。

6.4. 配置安全域的验证

要配置安全域的验证,请登录到管理控制台并遵循下列步骤。

过程 6.1. 为安全域设置验证

  1. 打开安全域的详细视图。

    点击管理控制台由上角的 Profiles 标签。在受管域里,从 Profile 视图左上角的 Profile 选择框里选择要修改的配置集。点击左侧的 Security 并点击展开菜单里的 Security Domains。点击你要编辑的安全域的 View 链接。
  2. 进入验证子系统配置。

    如果还未选择的话,点击视图顶部的 Authentication 标签。
    配置区域分成两部分:Login ModulesDetails。登录模块是配置的基本单元。安全域可以包含多个登录模块,每个都包括几个属性和选项。
  3. 添加一个验证模块。

    点击 Add 按钮来添加一个 JAAS 验证模块。输入相关的内容。Code 是模块的类名。Flags 控制模块如何和相同安全域里的其他验证模块交互。
    对标签的解释

    Java EE 6 规格提供了安全域的标签的解释。下面的列表来自 http://docs.oracle.com/javase/6/docs/technotes/guides/security/jaas/JAASRefGuide.html#AppendixA。关于更消息的信息,请参考这个文档。

    标签 详情
    Required
    登录模块是验证成功所必需的。如果成功或失败,验证都仍会继续处理登录模块列表。
    Requisite
    登录模块是验证成功所必需的。如果成功,验证将继续处理登录模块列表。如果失败,控制权马上返回给应用程序(验证不会继续处理登录模块列表)。
    Sufficient
    登录模块不是验证成功所必需的。如果成功,控制权马上返回给应用程序(验证不会继续处理登录模块列表)。如果失败,验证将继续处理登录模块列表。
    Optional
    登录模块不是验证成功所必需的。如果成功或失败,验证都仍会继续处理登录模块列表。
    在你添加了模块时,你可以通过屏幕上的 Details 里的 Edit 按钮修改它的 CodeFlags。请确保选择了 Attributes 标签页。
  4. 可选的:添加、编辑或删除模块选项。

    如果你需要在模块里添加选项,点击 Login Modules 列表里的条目,并选择 Details 部分的 Module Options 标签页,并提供选项的键和值。要编辑一个已存在的选项,点击它的键来进行修改。你可以使用 Remove 按钮来删除选项。
结果

你的验证模块已添加至安全域,且马上可为使用安全域的应用程序所用。

jboss.security.security_domain 模块选项

在默认情况下,安全域里定义的每个登录模块都会自动添加一个 jboss.security.security_domain 模块选项。这个选项会给检查是否只定义了已知选项的登录模块带来问题。IBM Kerberos 登录模块 com.ibm.security.auth.module.Krb5LoginModule 就是其中之一。

你可以通过在启动 JBoss EAP 时设置系统属性为 true 来禁用添加这个模块选项的行为。请添加下列内容到你的启动参数里。
-Djboss.security.disable.secdomain.option=true
你也可以用基于 web 的管理控制台来设置这个属性。在独立服务器里,你可以在配置文件的 Profile 部分来设置系统属性。在受管域里,你可以对每个服务器组设置系统属性。

6.5. 关于授权

授权是一个基于身份来赋予或拒绝对资源访问的机制。它作为一系列可以赋予 principal 的声明式安全角色来实现。
JBoss EAP 使用一个模块化系统来配置授权。每个安全域都可以获得一个或多个授权策略。每个策略都有一个定义自身行为的基本模块。它也可以通过特定的标记和属性来配置。配置授权子系统的最简单的方法是使用基于 web 的管理控制台。
授权不同于验证,它通常在验证之后发生。很多验证模块也可以处理授权。

6.6. 配置安全域里的授权

要配置安全域的授权,请登录到管理控制台并遵循下列步骤。

过程 6.2. 在安全域里设置授权

  1. 打开安全域的详细视图。

    点击管理控制台由上角的 Profiles 标签。在受管域里,从 Profile 视图左上角的 Profile 选择框里选择要修改的配置集。点击左侧的 Security 并点击展开菜单里的 Security Domains。点击你要编辑的安全域的 View 链接。
  2. 进入授权子系统配置。

    如果还未选择的话,点击视图顶部的 Authorization 标签。
    配置区域分成两部分:PoliciesDetails。登录模块是配置的基本单元。安全域可以包含几个授权策略,每个都包括几个属性和选项。
  3. 添加策略

    点击 Add 按钮来添加一个 JAAS 授权策略模块。输入相关的内容。Code 是模块的类名。Flags 控制模块如何和相同安全域里的其他授权模块交互。
    对标签的解释

    Java EE 6 规格提供了安全域的标签的解释。下面的列表来自 http://docs.oracle.com/javase/6/docs/technotes/guides/security/jaas/JAASRefGuide.html#AppendixA。关于更消息的信息,请参考这个文档。

    标签 详情
    要求的
    登录模块是授权成功所必需的。如果成功或失败,授权都仍会继续处理登录模块列表。
    Requisite
    登录模块是授权成功所必需的。如果成功,授权将继续处理登录模块列表。如果失败,控制权马上返回给应用程序(授权不会继续处理登录模块列表)。
    Sufficient
    登录模块不是授权成功所必需的。如果成功,控制权马上返回给应用程序(授权不会继续处理登录模块列表)。如果失败,授权将继续处理登录模块列表。
    Optional
    登录模块不是授权成功所必需的。如果成功或失败,授权都仍会继续处理登录模块列表。
    在你添加了模块时,你可以通过屏幕上的 Details 里的 Edit 按钮修改它的 CodeFlags。请确保选择了 Attributes 标签页。
  4. 可选的:添加、编辑或删除模块选项。

    如果你需要在模块里添加选项,点击 Login Modules 列表里的条目,并选择 Details 部分的 Module Options 标签页,并提供选项的键和值。要编辑一个已存在的选项,点击它的键来进行修改。你可以使用 Remove 按钮来删除选项。
结果

你的授权模块已添加至安全域,且马上可为使用安全域的应用程序所用。

6.7. 关于安全性审计

安全性审计包括触发事件,如在响应安全子系统里发生的事件而写入日志。审计机制和验证、授权、安全性映射细节都是作为安全域的一部分来配置的。
审计使用提供者模块。你可以使用内含的模块,或者实现自己的模块。

6.8. 配置安全审计

要配置安全域的安全审计,请登录到管理控制台并遵循下列步骤。

过程 6.3. 在安全域里设置安全审计

  1. 打开安全域的详细视图。

    点击管理控制台由上角的 Profiles 标签。在独立服务器里,标签页是 Profile。在受管域里,从 Profile 视图左上角的 Profile 选择框里选择要修改的配置集。点击左侧的 Security 并点击展开菜单里的 Security Domains。点击你要编辑的安全域的 View 链接。
  2. 进入审计子系统配置。

    如果还未选择的话,点击视图顶部的 Audit 标签。
    配置区域分成两部分:Provider ModulesDetails。提供者模块(Provider Module)是配置的基本单元。安全域可以包含多个提供者模块,每个都包括几个属性和选项。
  3. 添加一个提供者模块。

    点击 Add 按钮来添加一个提供者模块。在 Code 里输入提供者模块的类名。
    在你添加了模块时,你可以通过屏幕上的 Details 里的 Edit 按钮修改它的 Code。请确保选择了 Attributes 标签页。
  4. 检验你的模块是否可以运行

    审计模块的目的是提供一个在安全子系统里监控事件的途径。这种监控可以通过写入日志文件、电子邮件通知或其他可度量的审计机制来实现。
    例如,JBoss EAP 默认包含了 LogAuditProvider 模块。如果按照上面的步骤启用,这个审计模块会将安全通知写入 EAP_HOME 目录里的 log 子目录下的 audit.log 文件里。
    要检验上面的步骤是否可以在 LogAuditProvider 上下文里运行,你可以执行一个可能触发通知的动作并检查审计日志文件。
    关于安全审计提供者模块的完整列表,请参考 第 A.4 节 “包括的安全审计供应商模块”
  5. 可选的:添加、编辑或删除模块选项。

    如果你需要在模块里添加选项,点击 Modules 列表里的条目,并选择 Details 部分的 Module Options 标签页,并提供选项的键和值。要编辑一个已存在的选项,你可以使用 Remove 按钮来删除它,或者用正确的选项点击 Add 按钮再次添加它。
结果

你的安全审计模块已添加至安全域,且马上可为使用安全域的应用程序所用。

6.9. 关于安全性映射

安全性映射允许你在验证或授权发生后但在信息传递给应用程序之前合并验证和授权信息。其中一个例子是使用用于验证的 X509 证书,然后从证书转换 principal 为你的应用程序可以显示的逻辑名称。
你可以映射 principal(验证)、角色(授权)或凭证(非 principal 或角色的属性)。
角色映射用于在验证后对主题添加、替换或删除角色。
Principal 映射用于在验证后修改 principal。
属性映射用来从外部系统转换属性以供应用程序使用,反之亦然。

6.10. 在安全域里配置安全映射

要配置安全域的安全映射,请登录到管理控制台并遵循下列步骤。

过程 6.4. 在安全域里设置安全映射

  1. 打开安全域的详细视图。

    点击管理控制台由上角的 Profiles 标签。在独立服务器里,标签页是 Profile。在受管域里,从 Profile 视图左上角的 Profile 选择框里选择要修改的配置集。点击左侧的 Security 并点击展开菜单里的 Security Domains。点击你要编辑的安全域的 View 链接。
  2. 进入映射子系统配置。

    如果还未选择的话,点击视图顶部的 Mapping 标签。
    配置区域分成两部分:ModulesDetails。映射模块是配置的基本单元。安全域可以包含多个映射模块,每个都包括几个属性和选项。
  3. 添加一个模块。

    点击 Add 按钮来添加一个安全映射模块。输入模块的相关内容。Code 是模块的类名。Type 字段表示这个模块执行的映射的类型。所允许的值有 principal、role、attribute 和 credential。
    在你添加了模块时,你可以通过屏幕上的 Details 里的 Edit 按钮修改它的 CodeType。请确保选择了 Attributes 标签页。
  4. 可选的:添加、编辑或删除模块选项。

    如果你需要在模块里添加选项,点击 Modules 列表里的条目,并选择 Details 部分的 Module Options 标签页,并提供选项的键和值。要编辑一个已存在的选项,点击 Remove 标签键来删除它,并用新的值再次添加它。你可以使用 Remove 按钮来删除选项。
结果

你的安全映射模块已添加至安全域,且马上可为使用安全域的应用程序所用。

第 7 章 SSL 加密

7.1. 关于 SSL 加密

Secure Sockets Layer (SSL) 对两个系统间的网络通讯加密,它使用在握手时生成的一个双向密钥来加密。握手 连接阶段且只被这两个系统知晓。
为了双向加密密钥的安全交换,SSL 使用了公共密钥结构(PKI)。KPI 是一个加密方法,它利用了密钥对。 密钥对由两个独立但匹配的加密密钥 - 一个公共密钥和一个私有密钥。公共密钥被共享且用来加密数据,而私有密钥则保持私有且用来解密用公共密钥加密的数据。
当客户请求一个安全连接时,握手阶段在安全通讯开始前发生。在 SSL 握手期间,服务器将其公共密钥以证书形式发送给客户。这个证书包含服务器的标识(URL)、服务器的公共密钥以及一个是校验证书的数字签名。然后客户校验这个证书并决定证书是否值得信任。如果证书可以信任,那么客户将为 SLL 连接生成双向加密密钥,并用服务器的公共密钥进行加密,然后再发送回服务器。服务器用自己的私有密钥将这个双向加密密钥解密,而之后两个主机间的通讯将使用这个双向加密密钥进行加密。

7.2. 对 JBoss EAP Web 服务器实施 SSL 加密

简介

许多 web 应用程序都要求对客户端和服务器间的连接进行 SSL 加密,这也被称为 HTTPS 连接。你可以通过这个过程对服务器或服务器组启用 HTTPS

前提条件

  • 你需要一系列 SLL 加密密钥和加密证书。你可以从证书签名机构购买或者使用命令行工具来生成。关于使用红帽企业版 Linux 工具来生成加密密钥,请参考 第 7.3 节 “生成 SSL 密钥和证书”
  • 你需要知道你的环境和设置的下列细节:
    • 你的证书文件的完整目录名和路径
    • 你的加密密钥的密码。
  • 你需要运行管理 CLI 并连接到域控制器或独立服务器。

注意

这个过程使用适合用于受管域的 JBoss EAP 配置的命令。如果你使用的是独立服务器,请从任何管理 CLI 命令的开头将 /profile=default 删除。

过程 7.1. 配置 JBoss Web 服务器以使用 HTTPS

  1. 添加新的 HTTPS 连接器。

    执行下列管理 CLI 命令来修改配置。这会创建一个新的加密连接器,名为 HTTPS。它使用 https 模式、https 套接字绑定(默认为 8443 端口),且被设置为安全的。

    例 7.1. 管理 CLI 命令

    /profile=default/subsystem=web/connector=HTTPS/:add(socket-binding=https,scheme=https,protocol=HTTP/1.1,secure=true)
    
  2. 配置 SSL 加密证书和密钥。

    执行下列 CLI 命令来配置你的 SSL 证书,请用自己的值来替换例子里的值。这个例子假设密钥库被复制到服务器的配置目录,对于受管域来说,也就是EAP_HOME/domain/configuration/

    例 7.2. 管理 CLI 命令

    /profile=default/subsystem=web/connector=HTTPS/ssl=configuration:add(name=https,certificate-key-file=${jboss.server.config.dir}/keystore.jks,password=SECRET, key-alias=KEY_ALIAS)
    
    关于连接器的 SSL 属性参数的完整列表,请参考 第 7.4 节 “SSL 连接器引用”
  3. 部署应用程序。

    部署一个使用你已经配置好的配置集的应用程序到服务器组。如果你使用的是独立服务器,将这个应用程序部署至服务器。它的 HTTP 请求将使用新的 SSL 加密的连接。

7.3. 生成 SSL 密钥和证书

要使用 SSL 加密的 HTTP 协议(HTTPS)以及其他类型的通讯,你需要一个签名的加密证书。你可以从证书认证机构(CA)购买或者使用自签名(self-signed)的证书。许多第三方机构认为自签名的证书是不可靠的,但它适合于内部的测试目的。
这个过程让你使用红帽企业版 Linux 里的工具创建自签名的证书。

前提条件

  • 你需要 keytool 工具,任何 JDK 都提供它。红帽企业版 Linux 上的 OpenJDK 将这个命令安装在 /usr/bin/keytool
  • 请理解 keytool 命令的语法和参数。这个过程将使用非常浅显的说明,因为对 SSL 证书或 keytool 命令的深入讨论都超出了本文档的范畴。

过程 7.2. 生成 SSL 密钥和证书

  1. 用公共和私有密钥生成密钥库。

    运行下列命令来当前目录里生成带有别名 jboss 的名为server.keystore 的密钥库。
    keytool -genkey -alias jboss -keyalg RSA -keystore server.keystore -storepass mykeystorepass --dname "CN=jsmith,OU=Engineering,O=mycompany.com,L=Raleigh,S=NC,C=US"
    下表描述了用于 keytool 命令的参数:
    参数 描述
    -genkey keytool 命令生成包含公共和私有密钥的密钥对。
    -alias 密钥库的别名。这个值是任意的,但别名 jboss 是 JBoss Web 服务器使用的默认值。
    -keyalg 密钥对的生成算法。这个例子里是 RSA
    -keystore 密钥库文件的名称和位置。默认的位置是当前的目录。你可以选择任意名字。在这个例子里是 server.keystore
    -storepass 这个密码用于针对密钥库进行验证,从而读取密钥。这个密码长度必须至少为 6 且在访问时密钥库时提供。在这个例子里,我们使用 mykeystorepass。如果你忽略这个参数,在执行命令时你将被提示输入它。
    -keypass
    这是实际密钥的密码。

    注意

    由于实现的限制,它必须和库的密码相同。
    --dname 引号括起的描述密钥的可区分名称的字符串,如"CN=jsmith,OU=Engineering,O=mycompany.com,L=Raleigh,C=US"。这个字符串是下列组件的组合:
    • CN - 常用名或主机名。如果主机名是 "jsmith.mycompany.com",那么 CN 就是 "jsmith"。
    • OU - 机构单元,如 "Engineering"。
    • O - 机构名称,如 "mycompany.com"。
    • L - 地区,如 "Raleigh" 或 "London"。
    • S - 州或省,如 "NC"。这个参数是可选的。
    • C - 两个字符的国家代码,如 "US" 或 "UK"。
    当你执行上述命令时,你会被提示输入下列信息:
    • 如果你在命令行没有使用 -storepass 参数,你会被要求输入密钥库的密码。在下一次提示时再次输入新密码。
    • 如果你在命令行没有使用 -keypass 参数,你会被要求输入密钥密码。按 Enter 将其设置为和密钥密码相同的值。
    当命令执行完毕时,server.keystore 文件包含了带有别名 jboss 的单个密钥。
  2. 检验这个密钥。

    通过下列命令检验这个密钥是否正常工作。
    keytool -list -keystore server.keystore
    你会被提示输入密钥库密码。密钥库的内容将被显示(这个例子里是名为 jboss 的单个密钥)。请注意 jboss 密钥的类型是 keyEntry。这表示密钥库包含这个密钥的公共和私有条目。
  3. 生成一个证书签名请求。

    运行下列命令使用步骤一里创建的密钥库里的公共密钥来生成一个证书签名请求。
    keytool -certreq -keyalg RSA -alias jboss -keystore server.keystore -file certreq.csr
    系统会提示你输入密码以针对密钥库进行验证。然后 keytool 命令将在当前工作目录里创建一个名为 certreq.csr 的证书签名请求。
  4. 测试新生成的证书。

    通过下列命令测试证书的内容。
    openssl req -in certreq.csr -noout -text
    证书细节将被显示。
  5. 可选:提交证书到证书认证机构(CA)。

    证书机构(Certificate Authority,CA)可以验证你的证书,它被第三方的客户认为是可靠的。CA 向你提供一个签名的证书,还有一个或多个可选的中间证书。
  6. 可选:从密钥库导出自签名的证书

    如果你只需要用来进行测试或内部使用,你可以使用自签名的证书。你可以从密钥库导出在步骤一创建的证书:
    keytool -export -alias jboss -keystore server.keystore -file server.crt
    系统会提示你输入密码以针对密钥库进行验证。名为 server.crt 的自签名的证书将在当前工作目录里创建。
  7. 导入已签名的证书以及任何中间的证书。

    按照 CA 里说明的顺序导入每个证书。对于每个要导入的证书,请用实际的文件名替换 intermediate.caserver.crt。如果你的证书未作为独立的文件提供,请为每个证书创建一个独立的文件,并将其内容粘贴到文件里。

    注意

    你的已签名的证书和密钥都是有价值的资产。请小心地在服务器间传递。
    keytool -import -keystore server.keystore -alias intermediateCA -file intermediate.ca
    keytool -import -alias jboss -keystore server.keystore -file server.crt
  8. 测试你的证书是否已经成功导入。

    运行下列命令,遇提示时输入密钥库密码。密钥库的内容将被显示,证书将是列表里的一部分。
    keytool -list -keystore server.keystore
结果

你签名的证书现在已包含在密钥库里了,它可用来加密 SSL 连接,包括 HTTPS web 服务器通讯。

7.4. SSL 连接器引用

JBoss Web 连接器可能包括了下列 SSL 配置属性。提供的 CLI 命令是针对使用 default 配置集的受管域的。按照你的需要修改这个配置集名称(对于受管域),或者忽略命令行的 /profile=default 部分(对于独立服务器)。

表 7.1. SSL 连接器属性

属性 描述 CLI 命令
名称
SSL 连接器的显示名称。
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=name,value=https)
verify-client
设置为 true 表示在接受连接前需要客户的有效证书链。如果你希望 SSL 栈来请求客户证书,可以将其设为 want。设置为 false(默认值)表示不要求证书链,除非客户请求被使用 CLIENT-CERT 验证的安全约束保护的资源。
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=verify-client,value=want)
verify-depth
中间证书发行者在决定客户是否具有有效证书前检查的最多次数。默认值是 10
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=verify-depth,value=10)
certificate-key-file
保存服务器证书的密钥库文件的完整文件路径和名称。对于 JSSE 加密,这个证书文件将是唯一的,而 OpenSSL 则使用几个文件。默认值是运行 JBoss EAP 的用户的主目录下的 .keystore。如果你的 keystoreType 没有使用文件,请将这个参数设置为空字符串。
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=certificate-key-file,value=../domain/configuration/server.keystore)
certificate-file
如果你使用 OpenSSL 加密,请设置这个参数的值为包含服务器证书的文件的路径。
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=certificate-file,value=server.crt)
password
用于信任库和密钥库的密码。默认值是 changeit,你必须修改它,使其匹配密钥库的密码,从而可以正常使用配置。
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=password,value=changeit)
protocol
要使用的 SSL 协议的版本。支持的值包括 SLv2SSLv3TLSv1SSLv2+SSLv3ALL。默认的是 ALL
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=protocol,value=ALL)
cipher-suite
用逗号隔开的所允许的加密密码的列表。JSSE 的默认 JVM 包含不应该使用的弱密码。这个例子只列出了两个可能的密码,但实际的例子可能使用更多。
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=cipher-suite, value="TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA")
key-alias
用于密钥库里服务器证书的别名。默认值是 jboss
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=key-alias,value=jboss)
truststore-type
信任库的类型。不同的密钥库包括 PKCS12 和 Java 的标准 JKS
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=truststore-type,value=jks)
keystore-type
密钥库的类型那个。不同的密钥库类型包括 PKCS12 和 Java 的标准 JKS
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=keystore-type,value=jks)
ca-certificate-file
包含 CA 证书的文件。对于 JSSE 是 truststoreFile,并对密钥库使用相同密码。ca-certificate-file 文件被用来检验客户证书。
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=certificate-file,value=ca.crt)
ca-certificate-password
用于 ca-certificate-file 的证书密码。在下面的例子里,请用自己的掩码密码替换其中的密码。
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=ca-certificate-password,value=MASKED_PASSWORD)
ca-revocation-url
包含撤销列表的文件或 URL。它指向 crlFile(JSSE )或 SSLCARevocationFile(SSL)。
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=ca-revocation-url,value=ca.crl)
session-cache-size
SSL 会话缓存的大小。默认值是 0,它禁用会话缓存。
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=session-cache-size,value=100)
session-timeout
在缓存的 SSLSession 过期前的秒数。默认值为 86400 秒,也就是 24 小时。
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=session-timeout,value=43200)

第 8 章 安全区

8.1. 关于安全区

Security Realm(安全区)是用户和密码、用户和角色间一系列的映射。安全区是一个添加验证和授权到你的 EJB 和 Web 应用程序的机制。JBoss EAP 6 默认提供了两种安全区:
  • ManagementRealm 存储用于管理 API 的用户、密码和角色信息,它提供管理 CLI 和基于 web 的管理控制台的功能。它也为管理 JBoss EAP 自身提供了一个验证系统。如果你的应用程序需要用用于管理 API 的相同商业规则来验证,你也可以使用 ManagementRealm
  • ApplicationRealm 存储用于 Web 应用程序和 EJB 的用户、密码和角色信息。
每个区都存储在文件系统中的两个文件里:
  • REALM-users.properties 存储用户名和 hashed 密码。
  • REALM-users.roles 存储用户和角色的映射。
属性文件存储在 domain/configuration/standalone/configuration/ 目录里。这些文件由 add-user.shadd-user.bat 同时写入。当你运行这命令时,你的第一个决定是添加新用户到哪个区。

8.2. 添加新的安全区

  1. 运行管理 CLI。

    运行 jboss-cli.shjboss-cli.bat 命令并连接服务器。
  2. 创建新的安全区。

    运行下列命令在域控制器或独立服务器上创建一个名为 MyDomain 的安全区。
    /host=master/core-service=management/security-realm=MyDomainRealm:add()
  3. 创建对将保存新角色信息的属性文件的引用。

    运行下列命令创建一个名为 myfile.properties 的文件,它将包含附属新角色的属性。

    注意

    新创建的属性文件不是由内含的 add-user.shadd-user.bat 脚本管理的。它必须进行外部管理。
    /host=master/core-service=management/security-realm=MyDomainRealm/authentication=properties:add(path=myfile.properties)
结果

你的新安全区已被创建了。当你添加用户和角色到这个新的安全区时,信息将被存储在默认安全区外的一个单独的文件里。你可以用自己的应用程序或过程来管理这个新的文件。

8.3. 添加用户到安全区里

  1. 运行 add-user.shadd-user.bat 命令。

    打开一个命令行界面(command-line interface,CLI)。进入 EAP_HOME/bin/ 目录。如果你运行的是红帽企业版 LInux 或其他类 Unix 系统,请运行 add-user.sh。如果你运行的是 Microsoft Windows 服务器,则请运行 add-user.bat
  2. 选择是否添加管理用户或应用程序用户。

    对于这个过程,输入 b 来添加应用程序用户。
  3. 选择用户所添加至的安全区。

    在默认的情况下,唯一可用的安全区是 ApplicationRealm。如果你已经添加了一个自定义区,你可以输入它的名称。
  4. 在提示时输入用户名、密码和角色。

    在提示时输入想要的用户名、密码和可选角色。输入 yes 确认选择或 no 取消修改。所作修改将被写入到安全区的每个属性文件里。

第 9 章 子系统配置

9.1. 事务子系统的配置

9.1.1. 为 JTS 事务配置 ORB

在默认的 JBoss EAP 的安装里,ORB 是禁用的。你可以用命令行管理 CLI 启用 ORB。

注意

在受管域里,JacORB 子系统只能用于 fullfull-ha 配置集。在独立服务器里,你可以使用 standalone-full.xmlstandalone-full-ha.xml 配置。

过程 9.1. 使用管理控制台配置 ORB

  1. 查看配置集设置。

    从管理控制台的右上角选择 Profiles (受管域) 或 Profile(独立服务器)。如果你使用了受管域,请在左上角选择 fullfull-ha 配置集。
  2. 修改 Initializers 设置

    展开左侧的 Subsystems 菜单,展开 Container 子菜单并点击 JacORB
    在主屏幕上出现的表单里,选择 Initializers 标签页并点击 Edit 按钮。
    通过设置 Securityon 来启用安全拦截器。
    要启用 JTS 里的 ORB,请设置 Transaction Interceptors 值为 on,而不是默认的 spec
    关于这些值的详细解释,请点击表单里的 Need Help? 链接。在完成编辑后请点击 Save
  3. 高级的 ORB 配置

    关于高级的配置选项,请参考表单的其他部分。每个部分都包含一个关于参数详细解释的 Need Help? 链接。
使用管理 CLI 配置 ORB

你可以使用管理 CLI 配置 ORB 的每个方面。下面的命令配置初始器为与上面过程里使用管理控制台相同的值。这是 JTS 里 ORB 的最小配置。

这些命令是为使用 full 配置集的受管域配置的。如果有必要,请根据需要修改这个配置集。如果你使用了独立服务器,请忽略命令行的 /profile=full 部分。

例 9.1. 启用安全拦截器

/profile=full/subsystem=jacorb/:write-attribute(name=security,value=on)

例 9.2. 启用 JTS 里的 ORB

/profile=full/subsystem=jacorb/:write-attribute(name=transactions,value=on)

9.2. JMS 配置

9.2.1. 对 HornetQ 配置属性的引用

HornetQ 的 JBoss EAP 6 实现开放了下列可配置的属性。你可以使用管理 CLI 通过 read-resource 操作开放可配置或可查看的属性。

例 9.3. 示例

[standalone@localhost:9999 /] /subsystem=messaging/hornetq-server=default:read-resource

表 9.1. HornetQ 属性

属性 示例值 类型
allow-failback true BOOLEAN
async-connection-execution-enabled true BOOLEAN
backup false BOOLEAN
cluster-password somethingsecure STRING
cluster-user HORNETQ.CLUSTER.ADMIN.USER STRING
clustered false BOOLEAN
connection-ttl-override -1 LONG
create-bindings-dir true BOOLEAN
create-journal-dir true BOOLEAN
failback-delay 5000 LONG
failover-on-shutdown false BOOLEAN
id-cache-size 2000 INT
jmx-domain org.hornetq STRING
jmx-management-enabled false BOOLEAN
journal-buffer-size 100 LONG
journal-buffer-timeout 100 LONG
journal-compact-min-files 10 INT
journal-compact-percentage 30 INT
journal-file-size 102400 LONG
journal-max-io 1 INT
journal-min-files 2 INT
journal-sync-non-transactional true BOOLEAN
journal-sync-transactional true BOOLEAN
journal-type ASYNCIO STRING
live-connector-ref reference STRING
log-journal-write-rate false BOOLEAN
management-address jms.queue.hornetq.management STRING
management-notification-address hornetq.notifications STRING
memory-measure-interval -1 LONG
memory-warning-threshold 25 INT
message-counter-enabled false BOOLEAN
message-counter-max-day-history 10 INT
message-counter-sample-period 10000 LONG
message-expiry-scan-period 30000 LONG
message-expiry-thread-priority 3 INT
page-max-concurrent-io 5 INT
perf-blast-pages -1 INT
persist-delivery-count-before-delivery false BOOLEAN
persist-id-cache true BOOLEAN
persistence-enabled true BOOLEAN
remoting-interceptors undefined LIST
run-sync-speed-test false BOOLEAN
scheduled-thread-pool-max-size 5 INT
security-domain other STRING
security-enabled true BOOLEAN
security-invalidation-interval 10000 LONG
server-dump-interval -1 LONG
shared-store true BOOLEAN
started true BOOLEAN
thread-pool-max-size 30 INT
transaction-timeout 300000 LONG
transaction-timeout-scan-period 1000 LONG
version 2.2.16.Final (HQ_2_2_16_FINAL, 122) STRING
wild-card-routing-enabled true BOOLEAN

警告

journal-file-size 的值必须比发往服务器的消息大小要大,否则服务器无法存储这个消息。

第 10 章 Web、HTTP 连接器和 HTTP 群集

10.1. 配置 mod_cluster 工作节点

主节点只需要通过 mod_cluster 子系统配置一次。要配置 mod_cluster 子系统,请参考《管理和配置指南》里的『配置 mod_cluster 子系统』。每个工作节点都是独立配置的,所以你可以为每个要加入群集的节点重复这个步骤。
如果你使用了受管域,服务器组里的每个服务器都是一个工作节点,它们共享相同的配置。因此,配置是对于整个服务器组完成的。而在独立服务器里,配置是对于单个 JBoss EAP 实例完成的。其余的配置步骤是相同的。

工作节点配置

  • 如果你使用了独立服务器,它必须以 standalone-ha 配置集启动。
  • 如果你使用受管域,你的服务器组必须使用 hafull-ha 配置集,以及 ha-socketsfull-ha-sockets 套接字绑定组。JBoss EAP 附带满足这些要求的启用了群集的服务器组 other-server-group

注意

如果使用管理 CLI 命令,它会假设你使用受管域。如果你使用的是独立服务器,请从命令行删除 /profile=full-ha

过程 10.1. 配置工作节点

  1. 配置网络接口。

    在默认情况下,网络接口都是 127.0.0.1。每个容纳独立服务器或服务器组里的一个或多个服务器的物理主机的接口都需要进行配置以使用其他服务器可以看到的公共 IP 地址。
    要修改 JBoss EAP 主机的 IP 地址,你需要关闭它并直接修改配置文件。这是因为驱动管理控制台和管理 CLI 的 Management API 依赖于稳定的管理地址。
    遵循下列步骤将群集里的每个服务器的 IP 地址修改为主节点的公共 IP 地址。
    1. 完全地关闭服务器。
    2. 对于受管域,编辑位于 EAP_HOME/domain/configuration/ 里的 host.xml,而对于独立服务器,编辑位于 EAP_HOME/standalone/configuration/ 里的 standalone-ha.xml
    3. 找到 <interfaces> 元素。有三个接口需要配置,managementpublicunsecured。你都要修改 127.0.0.1 为主机的外部 IP 地址。
    4. 对于参与受管域但不是主节点的主机,找到 <host 元素。请注意,它没有结尾的 > 符号,这是因为它包含了属性。将其 name 属性从 master 修改为其他的唯一名称,每个从节点都应不同。这个名称也将被从节点用来标识群集,请注意这一点。
    5. 对于需要加入受管域的刚配置好的主机,找到 <domain-controller> 元素。注释或删除 <local /> 元素,并添加下列一行,修改 IP 地址(X.X.X.X)为域控制台的地址。这个步骤不适用于独立服务器。
      <remote host="X.X.X.X" port="${jboss.domain.master.port:9999}" security-realm="ManagementRealm"/>
      
    6. 保存文件并退出。
  2. 为每个从服务器配置验证。

    每个从服务器都需要在域控制器或独立主服务器的 ManagementRealm 里创建一个用户名和密码。在域控制器或独立主服务器上,运行 EAP_HOME/add-user.sh 命令。请用和从服务器相同的用户名添加一个用户到 ManagementRealm。当提示这个用户是否需要到外部的 JBoss AS 实例验证,请选择 yes。下面是这个命令的输入和输出的例子,从服务器名为 slave1,其密码为 changeme
    user:bin user$ ./add-user.sh
    
    What type of user do you wish to add? 
     a) Management User (mgmt-users.properties) 
     b) Application User (application-users.properties)
    (a): a
    
    Enter the details of the new user to add.
    Realm (ManagementRealm) : 
    Username : slave1
    Password : changeme
    Re-enter Password : changeme
    About to add user 'slave1' for realm 'ManagementRealm'
    Is this correct yes/no? yes
    Added user 'slave1' to file '/home/user/jboss-eap-6.0/standalone/configuration/mgmt-users.properties'
    Added user 'slave1' to file '/home/user/jboss-eap-6.0/domain/configuration/mgmt-users.properties'
    Is this new user going to be used for one AS process to connect to another AS process e.g. slave domain controller?
    yes/no? yes
    To represent the user add the following to the server-identities definition <secret value="Y2hhbmdlbWU=" />
    
  3. add-user.sh 的输出里复制 Base64 编码的 <secret> 元素。

    如果你计划验证时指定 Base64 编码的密码,请复制 add-user.sh 的输出里的 <secret> 元素值,你在下面的步骤里需要用到它。
  4. 修改从主机的安全区以使用新的验证。

    1. 重新打开从主机的 host.xmlstandalone-ha.xml 文件。
    2. 找到 <security-realms> 元素。这是你配置安全区(Security Realm)的地方。
    3. 你可以用下列方法之一指定 secret 值:
      • 在配置文件里指定 Base64 编码的密码值。

        1. <security-realm name="ManagementRealm"> 行下添加下列 XML 片段:
          <server-identities>
              <secret value="Y2hhbmdlbWU="/>
          </server-identities>
                          
          
          
        2. 用前一步骤的 add-user.sh 输出里返回的 secret 值来替换 "Y2hhbmdlbWU=" 。
      • 配置主机通过 vault.sh 获取密码。

        1. 使用 vault.sh 脚本生成一个加密的密码。它将生成一个这样的字符串: VAULT::secret::password::ODVmYmJjNGMtZDU2ZC00YmNlLWE4ODMtZjQ1NWNmNDU4ZDc1TElORV9CUkVBS3ZhdWx0
          你可以在本指南的『敏感字符串的密码阀』里找到更多信息:第 3.8.1 节 “关于保护明码文件里的敏感字符”
        2. <security-realm name="ManagementRealm"> 行下添加下列 XML 片段:
          <server-identities>
              <secret value="${VAULT::secret::password::ODVmYmJjNGMtZDU2ZC00YmNlLWE4ODMtZjQ1NWNmNDU4ZDc1TElORV9CUkVBS3ZhdWx0}"/>
          </server-identities>
                          
          
          
          请用前一步骤生成的加密密码替换 secret 值。

          注意

          当在阀里创建一个密码时,它必须以明文而不是 Base64 编码来指定。
      • 指定密码为系统属性。

        1. <security-realm name="ManagementRealm"> 行下直接添加下列 XML 代码块。
          <server-identities>
              <secret value=${server.identity.password}/>
          </server-identities>
                          
          
          
        2. 当你将密码指定为系统属性时,你可以用下列方法之一配置主机:
          • 在命令行里以明文输入密码来启动服务器,例如:
            -Dserver.identity.password=changeme

            注意

            密码必须以明文输入并对于任何执行 ps -ef 命令的用户可见。
          • 将密码放在属性文件里并将文件的 URL 作为命令行参数传入。
            1. 在属性文件里添加键/值对。例如:
              server.identity.password=changeme
              
            2. 用命令行参数启动服务器:
              --properties=URL_TO_PROPERTIES_FILE
              .
    4. 保存文件并退出。
  5. 重启服务器。

    从主机现在将以主机名为用户名以及加密的字符串为密码来向主服务器进行验证。

第 11 章 网络安全性

11.1. 保护管理接口

总结

在测试环境里,我们通常在由管理控制台、管理 CLI 和其他 API 实现组成的管理接口上不设置安全层来运行 JBoss EAP 6 。这可以允许快速的开发和配置修改。

此外,silent 验证模式也是默认的,这允许主机上的本地客户可以连接管理 CLI 而无需用户名和密码。这对于本地用户和管理 CLI 脚本是很方便的,但它可以根据需要禁用。其步骤请参考 。
当你开始测试并准备移至产品环境时,至少通过下列方式保护管理接口是极其重要的:

11.2. 指定 JBoss EAP 使用的网络接口

概述

隔离服务以使它们只被需要它们的客户访问,这样可以增强网络的安全性。JBoss EAP 在其默认配置里包含两个接口,两者都绑定到 IP 地址127.0.0.1localhost。其中一个被称为 management,它被管理控制台、CLI 和 API 使用。另外一个被称为 public,被用来部署应用程序。这些接口并不特殊或有什么含义,只是作为起点来提供。

management 接口默认使用端口 9990 和 9999,而public 接口使用端口 8080 或 8443(如果使用 HTTPS)。
你修改管理接口或公共接口的 IP 地址,或者两者都修改。

警告

如果你将管理接口开放给远程主机可以访问的其他网络接口,请注意潜在的安全问题。多数情况下,我们不建议为管理接口提供远程访问。
  1. 停止 JBoss EAP。

    以合适的方式对操作系统发送中断信号来停止 JBoss EAP。如果你以前台应用程序的方式运行 JBoss EAP ,那通常的做法是按 Ctrl+C 键。
  2. 重启 JBoss EAP,指定绑定地址。

    使用 -b 命令行选项在特定接口上启动 JBoss EAP。

    例 11.1. 指定公共接口。

    EAP_HOME/bin/domain.sh -b 10.1.1.1

    例 11.2. 指定管理接口。

    EAP_HOME/bin/domain.sh -bmanagement=10.1.1.1

    例 11.3. 为每个接口指定不同的地址。

    EAP_HOME/bin/domain.sh -bmanagement=127.0.0.1 -b 10.1.1.1

    例 11.4. 绑定公共接口到所有的网络接口上。

    EAP_HOME/bin/domain.sh -b 0.0.0.0
你也可以直接编辑你的 XML 配置文件以修改默认的绑定地址。然而,如果你这么做,你将不能使用 -b 命令行选项在运行时指定 IP 地址,所以我们不推荐这么做。如果确实要这么做,请在编辑 XML 文件前完全停止 JBoss EAP。

11.3. 配置和 JBoss EAP 6 一起使用的网络防火墙

总结

多数产品环境都使用防火墙作为总体网络安全策略的一部分。如果你需要多个 EAP 服务器来与其他 EAP 服务器或外部服务(如 Web 服务器或数据库)来通讯,你的防火墙需要考虑这一点。管理良好的防火墙只会打开操作所需的端口,它会限制对某些 IP 地址、子网和网络协议的端口的访问。

不过,关于防火墙的完整讨论超出了本文档的范畴。

前提条件

  • 确定你要打开的端口。请参考 第 11.4 节 “JBoss EAP 6使用的网络端口” 以确定端口列表。
  • 对防火墙软件的理解是必需的。在红帽企业版 Linux 6 里,这个过程会使用 system-config-firewall 命令。微软的 Windows 服务器包括了内置的防火墙,以及几个可用于任何平台的第三方的防火墙解决方案。
假设

这个过程配置防火墙是基于如下假设的:

  • 操作系统为红帽企业版 Linux 6。
  • EAP 运行在主机 10.1.1.2上。或者,EAP 服务器具有自己的防火墙。
  • 网络防火墙服务器运行在主机 10.1.1.1 的接口 eth0 上,且具有外部的接口 eth1
  • 你想把端口 5445(JMS 使用的端口)上的流量转发到 JBoss EAP 6。网络防火墙应该不允许其他流量通过。

过程 11.1. 管理和 JBoss EAP 6 一起使用的网络防火墙

  1. 登录到管理控制台。

    登录到管理控制台。在默认情况下,它运行在 http://localhost:9990/console/ 上。
  2. 确定套接字绑定组使用的套接字绑定。

    点击管理控制台右上方的 Profiles 标签。在屏幕的左侧会显示一系列菜单。菜单底部是 General Configuration。点击 Socket Binding GroupsSocket Binding Declarations 屏幕将会出现。你可以从右侧的复合框里来选择不同的组。

    注意

    如果你使用的是独立服务器,它只有一个套接字绑定组。
    套接字名称和端口将会出现,每页有 6 个值。你可以使用表下面的箭头来进行浏览。
  3. 确定你要打开的端口。

    根据特定端口的功能以及环境的需要,一些端口可能需要通过防火墙来访问。如果你不确定某个套接字绑定的目的,请参考 第 11.4 节 “JBoss EAP 6使用的网络端口” 里关于默认套接字绑定和其目的的列表。
  4. 配置防火墙将通讯转发到 JBoss EAP 6。

    执行这些步骤来配置网络防火墙以允许指定端口上的通讯。
    1. 登录到防火墙主机并以根用户身份打开命令行提示。
    2. 执行 system-config-firewall 来启动防火墙配置工具。GUI 或命令行工具根据你登录到防火墙系统的方式启动。这个任务假设你通过 SSH 登录并使用了命令行接口。
    3. 使用 TAB 键切换到 Customize 按钮,然后按 ENTER 键。Trusted Services 屏幕将会出现。
    4. 不要修改任何值,使用 TAB 键切换到 Forward 按钮,然后按 ENTER 键进入下一屏幕。Other Ports 屏幕将出现。
    5. 使用 TAB 键切换到 <Add> 按钮,然后按 ENTER 键。Port and Protocol 屏幕将会出现。
    6. Port / Port Range 字段里输入5445 ,然后使用 TAB 键切换到 Protocol 字段,并输入 tcp。使用 TAB 键切换到 OK 按钮,然后按 ENTER 键。
    7. 使用 TAB 键切换到 Forward 按钮,直至你到达 Port Forwarding 屏幕。
    8. 使用 TAB 键切换到 <Add> 按钮,然后按 ENTER 键。
    9. 填写下列值以设立端口映射到 5445。
      • 源接口:eth1
      • 协议:tcp
      • 端口 / 端口范围:5445
      • 目的 IP 地址:10.1.1.2
      • 端口 / 端口范围:5445
      使用 TAB 键切换到 OK 按钮,然后按 ENTER
    10. 使用 TAB 键切换到 Close 按钮,然后按 ENTER
    11. 使用 TAB 键切换到 OK 按钮,然后按 ENTER。要应用这些改动,阅读警告提示并点击 Yes
  5. 在你的 JBoss EAP 6 主机上配置防火墙。

    一些机构选择在 JBoss EAP 6 服务器上配置防火墙,并关闭非操作必需的端口。请参考 第 11.4 节 “JBoss EAP 6使用的网络端口” 并确定要打开的端口,然后关闭其他端口。红帽企业版 6 的默认配置关闭所有的端口,除了 22(用于 SSH)和 5253(用于多点传送 DNS)。当你在配置端口的时候,请确保你可以从物理上访问服务器,这样就不会不经意地锁住自己。
结果

配置了防火墙,它会按照你在防火墙配置里指定的将通讯转发到内部的 JBoss EAP 6 服务器。如果你选择在 EAP 服务器上启用防火墙,除了需要运行应用程序的端口,所有的端口将被关闭。

11.4. JBoss EAP 6使用的网络端口

JBoss EAP 6 的默认配置使用的端口取决于下列因素:
  • 你的服务器组是否使用了默认的套接字绑定组,或者自定义的套接字绑定组。
  • 单独部署的要求。

注意

你可以配置一个数字的端口偏移量,以减缓当在同一个服务服务器上运行多个服务器时端口冲突。如果你的服务器使用了数字的端口偏移量,在它的服务器组的套接字绑定组的默认端口上添加偏移量。例如,如果套接字绑定组的 HTTP 端口是 8080,而你的服务器使用了端口偏移量为 100,那么它的 HTTP 端口是 8180。
除非额外指定,这个端口将使用 TCP 协议。

默认的套接字绑定组

  • full-ha-sockets
  • full-sockets
  • ha-sockets
  • standard-sockets

表 11.1. 默认的套接字绑定组的引用

名称 端口 多点传送端口 描述 full-ha-sockets full-sockets ha-socket standard-socket
ajp 8009 Apache JServ 协议,用于 HTTP 群集和负载平衡。
http 8080 用于已部署应用程序的默认端口。
https 8443 已部署的应用程序和客户间的用 SSL 加密的连接。
jacorb 3528 用于 JTS 事务的 CORBA 服务和其他依赖于 ORB 的服务。
jacorb-ssl 3529 SSL 加密的 CORBA 服务。
jgroups-diagnostics 7500 多点传送。用于 HA 群集里的 Peer 发现。
jgroups-mping 45700 多点传送。用于在 HA 群集里发现初始成员资格。
jgroups-tcp 7600 HA 群集里使用 TCP 的多点传送 Peer 发现。
jgroups-tcp-fd 57600 用于 TCP 上的 HA 失败检测。
jgroups-udp 55200 45688 HA 群集里使用 UDP 的多点传送 Peer 发现。
jgroups-udp-fd 54200 用于 UDP 上的 HA 失败检测。
messaging 5445 JMS 服务。
messaging-group 被 HornetQ JMS 广播和发现组引用。
messaging-throughput 5455 JMS remoting 所使用的。
mod_cluster 23364 用于 JBoss EAP 和 HTTP 加载平衡器之间通讯的多点传送端口。
osgi-http 8090 由使用 OSGi 子系统的内部组件使用。
remoting 4447 用于远程 EJB 调用。
txn-recovery-environment 4712 JTA 事务恢复管理者。
txn-status-manager 4713 JTA / JTS 事务管理者。
管理端口

除了套接字绑定组,每个主机控制台都打开另外两个端口用于管理:

  • 9990 - Web 管理控制台的端口
  • 9999 - 管理控制台和 API 使用的端口

部分 III. 保证应用程序的安全

第 12 章 应用程序的安全性

12.1. 启用/禁用基于描述符的属性替换

警告

Topic 9089, Revision 431229 failed validation and is not included in this build.

12.2. 数据源安全性

12.2.1. 关于数据源安全性

数据源安全性的首选方案是使用安全域或密码阀。下面是它们各自的例子。关于更多的信息,请参考:

例 12.1. 安全域示例

<security>
   <security-domain>mySecurityDomain</security-domain>
</security>

例 12.2. 密码阀示例

<security>
  <user-name>admin</user-name>
  <password>${VAULT::ds_ExampleDS::password::N2NhZDYzOTMtNWE0OS00ZGQ0LWE4MmEtMWNlMDMyNDdmNmI2TElORV9CUkVBS3ZhdWx0}</password>
</security>

12.3. EJB 应用程序的安全性

12.3.1. 安全标识符

12.3.1.1. 关于 EJB 的安全标识符

安全标识符(Security Identity),也称为调用标识符(Invocation Identity),指的是安全配置里的 <security-identity> 标记。它表示其他 EJB 在调用组件上的方法时必须使用的标识符。
调用标识符可以是当前的调用者,或者是专有的角色。第一种情况会使用 <use-caller-identity> 标签,而第二种情况则使用 <run-as> 标签。
关于设置 EJB 的安全标识符的更多信息,请参考 第 12.3.1.2 节 “设置 EJB 的安全标识符”

12.3.1.2. 设置 EJB 的安全标识符

例 12.3. 设置 EJB 和其调用者相同的安全标识符

这个例子为 EJB 的方法调用设置了和当前调用者相同的安全标识符。如果你没有指定 <security-identity> 元素声明,这个行为是默认的。
<ejb-jar>
  <enterprise-beans>
	 <session>
		<ejb-name>ASessionBean</ejb-name>
		<!-- ... -->
		<security-identity>
		  <use-caller-identity/>
		</security-identity>
	 </session>
	 <!-- ... -->
  </enterprise-beans>
</ejb-jar>

例 12.4. 设置 EJB 的安全标识符为特定的角色

要设置安全标识符为特定角色,请使用<security-identity> 标签里的 <run-as><role> 标签。
<ejb-jar>
  <enterprise-beans>
	 <session>
		<ejb-name>RunAsBean</ejb-name>
		<!-- ... -->
		<security-identity>
		  <run-as>
			 <description>A private internal role</description>
			 <role-name>InternalRole</role-name>
		  </run-as>
		</security-identity>
	 </session>
  </enterprise-beans>
  <!-- ... -->
</ejb-jar>

在默认情况下,当你使用 <run-as> 时,名为 anonymous 的 principal 被分配给转出调用。要分配不同的 principal,请使用 <run-as-principal>
<session>
    <ejb-name>RunAsBean</ejb-name>
    <security-identity>
        <run-as-principal>internal</run-as-principal>
    </security-identity>
</session>

注意

你也可以使用 servlet 元素里的 <run-as><run-as-principal> 元素。

12.3.2. EJB 方法权限

12.3.2.1. 关于 EJB 方法权限(EJB Method Permission)

EJB 提供了一个 <method-permisison> 元素声明。这个声明设置了被允许调用 EJB 的接口方法的角色。你可以为下列组合指定权限:
  • 命名 EJB 的所有 home 和 component 接口方法
  • 命名 EJB 的 home 或 component 接口的一个指定的方法
  • 具有重载名的一系列方法里的一个指定的方法

12.3.2.2. 使用 EJB Method Permission

概述

<method-permission> 元素定义了被允许访问 <method> 定义的 EJB 方法的逻辑角色。下面的几个例子演示了 XML 的语法。其中多个 method permission 语句可能会出现并具有累积的效应。<method-permission> 元素是 <ejb-jar> 描述符里 <assembly-descriptor> 元素的一个子元素。

使用 EJB Method Permission 的注解之外的另一种方法是 XML 语法。

例 12.5. 允许角色访问 EJB 的所有方法:

<method-permission>
  <description>The employee and temp-employee roles may access any method
  of the EmployeeService bean </description>
  <role-name>employee</role-name>
  <role-name>temp-employee</role-name>
  <method>
    <ejb-name>EmployeeService</ejb-name>
    <method-name>*</method-name>
  </method>
</method-permission>
	

例 12.6. 允许角色访问 EJB 的某个方法,并限制可以传递哪些方法参数:

<method-permission>
  <description>The employee role may access the findByPrimaryKey,
  getEmployeeInfo, and the updateEmployeeInfo(String) method of
  the AcmekPayroll bean </description>
  <role-name>employee</role-name>
  <method>
	<ejb-name>AcmekPayroll</ejb-name>
	<method-name>findByPrimaryKey</method-name>
  </method>
  <method>
	<ejb-name>AcmePayroll</ejb-name>
	<method-name>getEmployeeInfo</method-name>
  </method>
  <method>
	<ejb-name>AcmePayroll</ejb-name>
	<method-name>updateEmployeeInfo</method-name>
	<method-params>
	  <method-param>java.lang.String</method-param>
	</method-params>
  </method>
</method-permission>

例 12.7. 允许经验证的用户访问 EJB 的方法

使用 <unchecked/> 元素可以允许任何经验证的用户使用指定的方法。
<method-permission>
  <description>Any authenticated user may access any method of the
  EmployeeServiceHelp bean</description>
  <unchecked/>
  <method>
	<ejb-name>EmployeeServiceHelp</ejb-name>
	<method-name>*</method-name>
  </method>
</method-permission>

例 12.8. 完全排除使用特定的 EJB 方法:

<exclude-list>
  <description>No fireTheCTO methods of the EmployeeFiring bean may be
  used in this deployment</description>
  <method>
	<ejb-name>EmployeeFiring</ejb-name>
	<method-name>fireTheCTO</method-name>
  </method>
</exclude-list>

例 12.9. 包含几个 <method-permission> 块的完整 <assembly-descriptor> 例子:

<ejb-jar>
    <assembly-descriptor>
        <method-permission>
            <description>The employee and temp-employee roles may access any
                method of the EmployeeService bean </description>
            <role-name>employee</role-name>
            <role-name>temp-employee</role-name>
            <method>
                <ejb-name>EmployeeService</ejb-name>
                <method-name>*</method-name>
            </method>
        </method-permission>
        <method-permission>
            <description>The employee role may access the findByPrimaryKey,
                getEmployeeInfo, and the updateEmployeeInfo(String) method of
                the AcmePayroll bean </description>
            <role-name>employee</role-name>
            <method>
                <ejb-name>AcmePayroll</ejb-name>
                <method-name>findByPrimaryKey</method-name>
            </method>
            <method>
                <ejb-name>AcmePayroll</ejb-name>
                <method-name>getEmployeeInfo</method-name>
            </method>
            <method>
                <ejb-name>AcmePayroll</ejb-name>
                <method-name>updateEmployeeInfo</method-name>
                <method-params>
                    <method-param>java.lang.String</method-param>
                </method-params>
            </method>
        </method-permission>
        <method-permission>
            <description>The admin role may access any method of the
                EmployeeServiceAdmin bean </description>
            <role-name>admin</role-name>
            <method>
                <ejb-name>EmployeeServiceAdmin</ejb-name>
                <method-name>*</method-name>
            </method>
        </method-permission>
        <method-permission>
            <description>Any authenticated user may access any method of the
                EmployeeServiceHelp bean</description>
            <unchecked/>
            <method>
                <ejb-name>EmployeeServiceHelp</ejb-name>
                <method-name>*</method-name>
            </method>
        </method-permission>
        <exclude-list>
            <description>No fireTheCTO methods of the EmployeeFiring bean may be
                used in this deployment</description>
            <method>
                <ejb-name>EmployeeFiring</ejb-name>
                <method-name>fireTheCTO</method-name>
            </method>
        </exclude-list>
    </assembly-descriptor>
</ejb-jar>

12.3.3. EJB 安全注解

12.3.3.1. 关于 EJB 安全注解

EJB 使用安全注解来传递安全信息到部署者。其中包括:
@DeclareRoles
声明哪些角色是可用的。
@SecurityDomain
指定用于 EJB 的安全域。如果 EJB 的授权用 @RolesAllowed 进行注解,授权将只在 EJB 用安全域注解时才会应用。
@RolesAllowed, @PermitAll, @DenyAll
指定哪些方法权限是被允许的。关于方法权限的更多信息,请参考 第 12.3.2.1 节 “关于 EJB 方法权限(EJB Method Permission)”
@RolesAllowed, @PermitAll, @DenyAll
指定哪些方法权限是被允许的。关于方法权限的更多信息,请参考 第 12.3.2.1 节 “关于 EJB 方法权限(EJB Method Permission)”
@RunAs
配置组件的传播的安全标识符。
关于更多的信息,请参考 第 12.3.3.2 节 “使用 EJB 安全注解”

12.3.3.2. 使用 EJB 安全注解

概述

你可以使用 XML 描述符或注解来控制哪些安全角色能够调用你的 EJB 里的方法。关于使用 XML 描述符的更多信息,请参考 第 12.3.2.2 节 “使用 EJB Method Permission”

控制 EJB 的安全权限的注解

@DeclareRoles
使用 @DeclareRoles 来定义针对哪些安全角色检查权限。如果没有使用 @DeclareRoles,这个列表将自动根据 @RolesAllowed 注解进行构建。
@SecurityDomain
指定用于 EJB 的安全域。如果 EJB 的授权用 @RolesAllowed 进行注解,授权将只在 EJB 用安全域注解时才会应用。
@RolesAllowed, @PermitAll, @DenyAll
使用 @RolesAllowed 来列出哪些角色别允许访问某个或某些方法。使用 @PermitAll 或 @DenyAll 来允许或拒绝所有的角色使用某个或某些方法。
@RunAs
使用 @RunAs 来指定总是以之运行的某个角色或方法。

例 12.10. 安全性注解示例

@Stateless
@RolesAllowed({"admin"})
@SecurityDomain("other")
public class WelcomeEJB implements Welcome {
	@PermitAll
	public String WelcomeEveryone(String msg) {
		return "Welcome to " + msg;
	}
	@RunAs("tempemployee")
	public String GoodBye(String msg) {
	    return "Goodbye, " + msg;
	}
	public String
	public String GoodbyeAdmin(String msg) {
		return "See you later, " + msg;
	}
}
在这段代码里,所有的角色都可以访问方法 WelcomeEveryone GoodBye 方法以 tempemployee 角色运行。只有 admin 角色可以访问方法 GoodbyeAdmin,以及其他任何没有使用安全性注解的的方法。

12.3.4. 对 EJB 的远程访问

12.3.4.1. 关于远程方法访问

JBoss Remoting 是提供对 EJB、JMX MBean 和其他类似服务的远程访问的框架。它以下列传输类型运行,带有或不带有 SSL:

支持的传输类型

  • Socket / Secure Socket
  • RMI / RMI over SSL
  • HTTP / HTTPS
  • Servlet / Secure Servlet
  • Bisocket / Secure Bisocket
JBoss Remoting 也支持通过多点传送或 JNDI 的自动发现。
它被 JBoss EAP 里的多个子系统使用,它让你设计、实施和部署客户可以通过不同的传输机制来远程调用的服务。它也允许你访问 JBoss EAP 里现有的服务。
数据编码

Remoting 系统也提供数据编码(Data Marshalling)和解码(Unmarshalling)服务。数据编码指的是在网络间和平台边界安全移动数据的能力,这样独立的系统就可以在上面执行任务。然后这个任务被送回原来的系统,就像是在本地处理的一样。

架构概述

当你设计一个使用 Remoting 的客户应用程序时,通过配置它使用特定的资源定位器 InvokerLocator(它是一个 URL 类型格式的简单 String 对象),你可以指引应用程序和服务器通讯。服务器侦听作为 remoting 子系统的一部分配置的 connector 上的远程资源的请求 。connector 将请求传给配置好的 ServerInvocationHandler。每个 ServerInvocationHandler 都实现了一个 invoke(InvocationRequest) 方法,它知道如何处理这个请求。

JBoss Remoting 框架包含三个层次,它们在客户和服务器端彼此镜像。

JBoss Remoting 框架层次

  • 用户和 outer 层交互。在客户端,outer 层是 Client 类,它发送调用请求。在服务器端,它是 InvocationHandler,由用户实现并接收调用请求。
  • 传输是由调用层控制的。
  • 最底层包含 mashaller 和 unmashaller,它们将 mats 数据转换为线格式(Wire Format)。

12.3.4.2. 关于远程回调

当远程客户从服务器请求信息时,它可以阻塞并等待服务器应答,单这通常不是理想的办法。要允许客户侦听服务器上异步的事件,并再等待服务器完成请求的同时继续其他任务,你的应用程序可以请求服务器在完成时发送一个通知。这被成为回调(Callback)。客户也可以代替其他客户添加自己为生成的异步事件的侦听器。对于如何接收回调由两种不同的选择:pull 和 push。客户同步地检查 pull 回调,但被动地侦听 push 回调。
基本上,回调通过服务器发送一个 InvocationRequest 给客户端。服务器端的代码与之相同而不管回调是异步还是同步的。只有客户需要知道这个区别。服务器的 InvocationRequest 发送一个 responseObject 给客户端。这是客户端已经请求的负载。这可以是一个对请求的直接响应或事件通知。
你的服务器也使用 m_listeners 来追踪侦听器。它包含一个已添加至你的服务器处理程序里的侦听器列表。ServerInvocationHandler 接口包含允许你管理这个列表的方法。
客户端以不同的方式处理 pull 和 push 回调。但两种情况下它都必须实现一个回调处理器(Callbak Handler)。回调处理器是对 org.jboss.remoting.InvokerCallbackHandler 接口的实现,它处理回调数据。在实现了回调处理器后,你可以将自己添加为 pull 回调的侦听器,或者实现一个回调服务器来处理 push 回调。
Pull 回调

对于 pull 回调,你的客户端通过 Client.addListener() 方法将自己添加到服务器的侦听器列表里。然后,它定期轮询服务器的同步回调数据递送。轮询是通过 Client.getCallbacks() 进行的。

Push 回调

Push 回调要求你的客户端应用程序运行自己的 InvocationHandler。为此,你需要在客户端运行一个 Remoting 服务。这被称为回调服务器(Callback Server)。回调服务器异步接受转入请求并为请求者(这个例子里是服务器)处理它们。要在主服务器里注册你的客户的回调服务器,请将回调服务器的 InvokerLocator 作为第二个参数传入 addListener 方法。

12.3.4.3. 关于远程服务器检测

远程服务器和客户可以用 JNDI 或多点传送自动检测到彼此。远程检测器(Remoting Detector)将添加到客户和服务器上,而客户端将添加网络注册表(Network Registry)。
服务器端的检测器会定期地扫描 InvokerRegistry 并轮询已创建的所有服务器调用者。它使用这些信息来发布包含每个服务器调用者支持的定位器和子系统的检测消息。它通过多点传送或绑定到 JNDI 服务器来发布这个消息。
在客户端,检测器接收到多点传送消息或定期地轮询 JNDI 服务器来获取检测消息。如果检测器注意到检测消息是关于最近检测到的远程服务器,它会将此注册到网络注册表(NetworkRegistry)。如果检测到某个服务器不再可用,检测器也会更新网络注册。

12.3.4.4. 配置 Remoting 子系统

概述

JBoss Remoting 有三个顶层的可配置元素:工作者线程池、一个或多个连接器以及一系列本地和远程的连接 URL。本主题解释了每个可配置条目、配置这些条目的示例 CLI 命令,以及一个完整配置子系统的 XML 示例。这个配置只适用于服务器。多数情况下都根本不需要配置 Remoting 子系统,除非你使用了自定义的连接器。作为 Remoting 客户的应用程序,如 EJB,都需要单独的配置来连接专有的连接器。

注意

Remoting 子系统配置没有开放给基于 Web 的管理控制台,但你可以通过命令行的管理 CLI 来配置。我们不推荐手动编辑 XML。
应用 CLI 命令

当配置 default 配置集时,CLI 命令格式是对应受管域的。如要配置不同的配置集,请替换它的名称。对于独立服务器,请忽略命令里的 /profile=default 部分。

Remoting 子系统外部的配置

Remoting 子系统外部的一些配置是:

网络接口
Remoting 子系统使用的网络接口是domain/configuration/domain.xmlstandalone/configuration/standalone.xml 里定义的 unsecure 接口。
<interfaces>
   <interface name="management"/>
   <interface name="public"/>
   <interface name="unsecure"/>
</interfaces>        
            

unsecure 接口的 per-host 定义是在和 domain.xmlstandalone.xml 相同目录里的 host.xml 里定义的。其他几个子系统也使用了这个接口。修改它时请小心。
<interfaces>
   <interface name="management">
      <inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
   </interface>
   <interface name="public">
      <inet-address value="${jboss.bind.address:127.0.0.1}"/>
   </interface>
   <interface name="unsecure">
      <!-- Used for IIOP sockets in the standard configuration.
         To secure JacORB you need to setup SSL -->
      <inet-address value="${jboss.bind.address.unsecure:127.0.0.1}"/>
   </interface>
</interfaces>             
                 

socket-binding
remoting 使用的默认 socket-binding 绑定在 TCP 端口 4777。如果你要修改它,请参阅文档以获得关于套接字绑定和套接字绑定组的更多信息。
EJB 的 Remoting 连接器引用
EJB 子系统包含对 remoting 连接器的引用以用于远程方法调用。下面是默认的配置:
<remote connector-ref="remoting-connector" thread-pool-name="default"/>            
            

安全传输配置
在客户请求安全连接时,Remoting 传输使用 StartTLS 来使用 HTTPS、Secure Servlet 等。安全和非安全的连接都使用相同的套接字绑定(网络端口),所以不需要额外的服务器端配置。客户按照需要请求安全或非安全传输时。使用 Remoting 的 JBoss 企业级应用程序平台组件,如 EJB、ORB、JMS 提供者,在默认情况下都使用安全的接口。

警告

StartTLS 在客户请求连接时激活安全连接,否则默认就使用未设置安全性的连接。它本来就容易受到 Man in the Middle 风格的开发的影响, 因为攻击者可以拦截客户的请求并修改它来请求非安全的连接。如果客户没有接收安全连接,除非这个非安全的连接实际上是一个合适的 fall-back,否则都必须在日志里进行登记。
工作者线程池

工作者线程池是处理来自 Remoting 连接器的任务的线程组。它是一个单一的元素 <worker-thread-pool>,并使用几个属性。如果你遇到网络超时、线程用尽或需要限制内存使用,你可以调整这些属性。其推荐值取决于特定的环境。更多的信息,请联系红帽全球支持服务。

表 12.1. 工作者线程池属性

属性 描述 CLI 命令
read-threads
为远程 worker 创建的读线程数目。默认为 1
/profile=default/subsystem=remoting/:write-attribute(name=worker-read-threads,value=1)
write-threads
为远程 worker 创建的写线程数目。默认为 1
/profile=default/subsystem=remoting/:write-attribute(name=worker-write-threads,value=1)
task-keepalive
非核心 remoting worker 任务保持活动状态所需的毫秒数。默认为 60
/profile=default/subsystem=remoting/:write-attribute(name=worker-task-keepalive,value=60)
task-max-threads
用于 remoting worker 任务线程池的线程的个数。默认为 16
/profile=default/subsystem=remoting/:write-attribute(name=worker-task-max-threads,value=16)
task-core-threads
用于 remoting worker 任务线程池的内核个数。默认为 4
/profile=default/subsystem=remoting/:write-attribute(name=worker-task-core-threads,value=4)
task-limit
在拒绝前允许的 remoting worker 任务的最大个数。默认为 16384
/profile=default/subsystem=remoting/:write-attribute(name=worker-task-limit,value=16384)
连接器

连接器是主要的远程配置元素。可以存在多个连接器。每个都由一个 <connector> 元素、几个子元素以及一些属性组成。默认的连接器由几个 JBoss EAP 的子系统使用。对自定义连接器的元素和属性的设置取决于你的应用程序,更多的信息请联系红帽全球支持服务。

表 12.2. 连接器属性

属性 描述 CLI 命令
name JNDI 使用的连接器的名称。
/profile=default/subsystem=remoting/connector=remoting-connector/:write-attribute(name=name,value=remoting-connector)
socket-binding 这个连接器使用的套接字绑定的名称。
/profile=default/subsystem=remoting/connector=remoting-connector/:write-attribute(name=socket-binding,value=remoting)
authentication-provider
和这个连接器一起使用的 Java Authentication Service Provider Interface for Containers (JASPIC)。这个模块必须位于 classpath 里。
/profile=default/subsystem=remoting/connector=remoting-connector/:write-attribute(name=authentication-provider,value=myProvider)
security-realm
可选的。包含应用程序的用户、密码和角色的安全区。EJB 或 Web 应用程序可以针对安全区进行验证。默认的 JBoss 企业级应用程序平台安装里 ApplicationRealm 是可用的。
/profile=default/subsystem=remoting/connector=remoting-connector/:write-attribute(name=security-realm,value=ApplicationRealm)

表 12.3. 连接器元素

属性 描述 CLI 命令
sasl
用于简单验证和安全层(Simple Authentication and Security Layer,SASL)验证机制的封装元素
N/A
properties
包含一个或多个 <property> 元素,每个都带有一个 name 属性和一个可选的 value 属性。
/profile=default/subsystem=remoting/connector=remoting-connector/property=myProp/:add(value=myPropValue)
转出的连接

你可以指定三种不同类型的转出连接:

  • 到 URI 的转出连接。
  • 本地的转出连接 – 连接至本地资源,如套接字。
  • 远程转出连接 – 连接至远程资源并使用安全区进行验证。
所有的转出连接都封装在 <outbound-connections> 元素里。这些连接类型都使用了一个 outbound-socket-binding-ref 属性。outbound-connection 使用了一个 uri 属性。远程的转出连接使用可选的 usernamesecurity-realm 属性以用于授权。

表 12.4. 转出连接的元素

属性 描述 CLI 命令
outbound-connection 普通的转出连接。
/profile=default/subsystem=remoting/outbound-connection=my-connection/:add(uri=http://my-connection)
local-outbound-connection 带有隐含的 local:// URI 模式的转出连接。
/profile=default/subsystem=remoting/local-outbound-connection=my-connection/:add(outbound-socket-binding-ref=remoting2)
remote-outbound-connection
用于 remote:// URI 模式的转出连接,它使用安全区的 basic/digest 验证。
/profile=default/subsystem=remoting/remote-outbound-connection=my-connection/:add(outbound-socket-binding-ref=remoting,username=myUser,security-realm=ApplicationRealm)
SASL 元素

在定义每个 SASL 子元素之前,你需要创建初始的 SASL 元素。请使用下列命令:

/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl:add
下表描述了 SASL 元素的子元素。
属性 描述 CLI 命令
include-mechanisms
包含一个 value 属性,它是一个以空格隔开的 SASL 机制的列表。
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl:write-attribute(name=include-mechanisms,value=["DIGEST","PLAIN","GSSAPI"])
qop
包含一个 value 属性,它是一个以空格隔开的 SASL Quality of Protection 值的列表,以降序排列。
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl:write-attribute(name=qop,value=["auth"])
strength
包含一个 value 属性,它是一个以空格隔开的 SASL 计算强度值的列表,以降序排列。
/subsystem=remoting/connector=remoting-connector/security=sasl:write-attribute(name=strength,value=["medium"])
reuse-session
包含一个布尔值的 value 属性。如果为 true,它将试图重用会话。
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl:write-attribute(name=reuse-session,value=false)
server-auth
包含一个布尔值的 value 属性。如果为 true,服务器将验证客户。
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl:write-attribute(name=server-auth,value=false)
policy
包含零或多个下列元素的封装元素,其中每个元素都使用单个的 value
  • forward-secrecy – 机制是否被要求实现向前保密(泄露某个会话不会自动为泄露以后的会话提供信息)。
  • no-active – 是否允许易受非字典攻击的机制。false 表示允许,true 表示拒绝。
  • no-anonymous – 是否允许接受匿名登录的机制。false 表示允许,true 表示拒绝。
  • no-dictionary – 是否允许易受被动字典攻击的机制。false 表示允许,true 表示拒绝。
  • no-plain-text – 是否允许易受简单明文被动攻击的机制。false 表示允许,true 表示拒绝。
  • pass-credentials – 是否允许传递客户凭证的机制。
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl/sasl-policy=policy:add
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl/sasl-policy=policy:write-attribute(name=forward-secrecy,value=true)
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl/sasl-policy=policy:write-attribute(name=no-active,value=false)
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl/sasl-policy=policy:write-attribute(name=no-dictionary,value=true)
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl/sasl-policy=policy:write-attribute(name=no-plain-text,value=false)
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl/sasl-policy=policy:write-attribute(name=pass-credentials,value=true)
properties
包含一个或多个 <property> 元素,每个都带有一个 name 属性和一个可选的 value 属性。
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl/property=myprop:add(value=1)
/profile=default/subsystem=remoting/connector=remoting-connector/security=sasl/property=myprop2:add(value=2)

例 12.11. 配置示例

这个例子展示了 JBoss 企业级应用程序平台 6 附带的默认 remoting 子系统。
<subsystem xmlns="urn:jboss:domain:remoting:1.1">
    <connector name="remoting-connector" socket-binding="remoting" security-realm="ApplicationRealm"/>
</subsystem>    
    

这个例子包含许多假想值,它也使用了前面讨论的元素和属性。
<subsystem xmlns="urn:jboss:domain:remoting:1.1">
    <worker-thread-pool read-threads="1" task-keepalive="60' task-max-threads="16" task-core-thread="4" task-limit="16384" write-threads="1" />
    <connector name="remoting-connector" socket-binding="remoting" security-realm="ApplicationRealm">
        <sasl>
            <include-mechanisms value="GSSAPI PLAIN DIGEST-MD5" />
            <qop value="auth" />
            <strength value="medium" />
            <reuse-session value="false" />
            <server-auth value="false" />
            <policy>
                <forward-secrecy value="true" />
                <no-active value="false" />
                <no-anonymous value="false" />
                <no-dictionary value="true" />
                <no-plain-text value="false" />
                <pass-credentials value="true" />
            </policy>
            <properties>
                <property name="myprop1" value="1" />
                <property name="myprop2" value="2" />
            </properties>
        </sasl>
        <authentication-provider name="myprovider" />
        <properties>
            <property name="myprop3" value="propValue" />
        </properties>
    </connector>
    <outbound-connections>
        <outbound-connection name="my-outbound-connection" uri="htt\
p://myhost:7777/"/>
        <remote-outbound-connection name="my-remote-connection" outbound-socket-binding-ref="my-remote-socket" username="myUser" security-realm="ApplicationRealm"/>
        <local-outbound-connection name="myLocalConnection" outbound-socket-binding-ref="my-outbound-socket"/>
    </outbound-connections>
</subsystem>    
    

Configuration Aspects Not Yet Documented

  • JNDI 和多点传送的自动检测

12.3.4.5. 对远程 EJB 客户使用安全区

安全区(security realm)是添加安全性到远程调用 EJB 的客户端的一个方法。安全区是一个简单的数据库,它由用户名/密码对以及用户名/角色对组成。这个术语也用在 web 容器的上下文里,其意思稍有不同。
要根据存在于安全区里的特定用户名和密码来验证 EJB,请使用这些步骤:
  • 添加新的安全区到域控制器或独立服务器里。
  • 添加下列参数到位于应用程序的 classpath 上的 jboss-ejb-client.properties 文件里。这个例子假设文件里的其他参数引用的连接是 default
    remote.connection.default.username=appuser
    remote.connection.default.password=apppassword
    
  • 在域或独立服务器上创建一个使用新的安全区的自定义远程连接器。
  • 部署 EJB 到使用带有自定义远程连接器的服务器组里,如果你使用的不是受管域则部署到独立服务器里。

12.3.4.6. 添加新的安全区

  1. 运行管理 CLI。

    运行 jboss-cli.shjboss-cli.bat 命令并连接服务器。
  2. 创建新的安全区。

    运行下列命令在域控制器或独立服务器上创建一个名为 MyDomain 的安全区。
    /host=master/core-service=management/security-realm=MyDomainRealm:add()
  3. 创建对将保存新角色信息的属性文件的引用。

    运行下列命令创建一个名为 myfile.properties 的文件,它将包含附属新角色的属性。

    注意

    新创建的属性文件不是由内含的 add-user.shadd-user.bat 脚本管理的。它必须进行外部管理。
    /host=master/core-service=management/security-realm=MyDomainRealm/authentication=properties:add(path=myfile.properties)
结果

你的新安全区已被创建了。当你添加用户和角色到这个新的安全区时,信息将被存储在默认安全区外的一个单独的文件里。你可以用自己的应用程序或过程来管理这个新的文件。

12.3.4.7. 添加用户到安全区里

  1. 运行 add-user.shadd-user.bat 命令。

    打开一个命令行界面(command-line interface,CLI)。进入 EAP_HOME/bin/ 目录。如果你运行的是红帽企业版 LInux 或其他类 Unix 系统,请运行 add-user.sh。如果你运行的是 Microsoft Windows 服务器,则请运行 add-user.bat
  2. 选择是否添加管理用户或应用程序用户。

    对于这个过程,输入 b 来添加应用程序用户。
  3. 选择用户所添加至的安全区。

    在默认的情况下,唯一可用的安全区是 ApplicationRealm。如果你已经添加了一个自定义区,你可以输入它的名称。
  4. 在提示时输入用户名、密码和角色。

    在提示时输入想要的用户名、密码和可选角色。输入 yes 确认选择或 no 取消修改。所作修改将被写入到安全区的每个属性文件里。

12.3.4.8. 关于使用 SLL 加密的远程 EJB 访问

在默认情况下,远程方法调用(Remote Method Invocation,RMI)和 EJB 3 Bean 的网络通讯是不加密的。当需要加密时,你可以使用 SSL,这样客户和服务器间的连接就被加密了。使用 SSL 还可以允许网络通讯通过阻止了 RMI 端口的防火墙。

12.4. JAX-RS 应用程序的安全性

12.4.1. 为 RESTEasy JAX-RS Web 服务启用基于角色的安全性

总结

RESTEasy 支持 JAX-RS 方法上的 @RolesAllowed、@PermitAll 和 @DenyAll 注解。然而,在默认情况下它并不承认这些注解。请遵循这些步骤来配置 web.xml 文件并启用基于角色的安全性。

警告

如果应用程序使用了 EJB,请不要机或基于角色的安全性。EJB 容器而不是 RESTEasy 将提供这个功能。

过程 12.1. 为 RESTEasy JAX-RS Web 服务启用基于角色的安全性

  1. 在文本编辑器里打开应用程序的 web.xml 文件。
  2. 添加下列 <context-param> 到文件的 web-app 标签下:
    <context-param>
        <param-name>resteasy.role.based.security</param-name>
        <param-value>true</param-value>
    </context-param>
    
    
  3. 使用 <security-role> 标签声明在 RESTEasy JAX-RS WAR 文件里使用的角色:
    <security-role><role-name>ROLE_NAME</role-name></security-role><security-role><role-name>ROLE_NAME</role-name></security-role>
        
    
    
    
    
  4. 为所有角色授权对 JAX-RS 运行时处理的所有 URL 的访问:
    <security-constraint><web-resource-collection><web-resource-name>Resteasy</web-resource-name><url-pattern>/PATH</url-pattern></web-resource-collection><auth-constraint><role-name>ROLE_NAME</role-name><role-name>ROLE_NAME</role-name></auth-constraint></security-constraint>
        
    	
    	
        
        
    	
    	
    
    
结果

这个应用程序里已经启用了基于角色的安全性并定义了一系列角色。

例 12.12. 基于角色的安全性配置示例

<web-app>

    <context-param>
	<param-name>resteasy.role.based.security</param-name>
	<param-value>true</param-value>
    </context-param>

    <servlet-mapping>
	<servlet-name>Resteasy</servlet-name>
	<url-pattern>/*</url-pattern>
    </servlet-mapping>

    <security-constraint>
	<web-resource-collection>
	    <web-resource-name>Resteasy</web-resource-name>
	    <url-pattern>/security</url-pattern>
	</web-resource-collection>
	<auth-constraint>
	    <role-name>admin</role-name>
	    <role-name>user</role-name>
	</auth-constraint>
    </security-constraint>

    <security-role>
	<role-name>admin</role-name>
    </security-role>
    <security-role>
	<role-name>user</role-name>
    </security-role>
    
</web-app>

12.4.2. 使用注解保护 JAX-RS Web 服务

总结

这个主题涵盖了使用支持的安全注解来保护 JAX-RS Web 服务的安全。

过程 12.2. 使用支持的安全注解保护 JAX-RS Web 服务

  1. 启用基于角色的安全性。更多信息请参考:第 12.4.1 节 “为 RESTEasy JAX-RS Web 服务启用基于角色的安全性”
  2. 添加安全注解到 JAX-RS Web 服务里。RESTEasy 支持下列注解:
    @RolesAllowed
    定义哪个角色可以访问这个方法。所有的角色都应该在 web.xml 文件里定义。
    @PermitAll
    允许 web.xml 文件里定义的所有角色访问这个方法。
    @DenyAll
    拒绝所有对这个方法的访问。

12.5. 安全远程密码协议

12.5.1. 关于安全远程密码协议

安全远程密码(Secure Remote Password,SRP)协议是互联网标准工作组 RFC2945 里描述的公共密钥交换握手的实现。RFC2945 的摘要是:
这个文档描述了一个强加密的的网络验证机制:安全远程密码(Secure Remote Password,SRP)协议。这个机制适合于使用用户提供的密码来协商安全连接,从而消除传统的和可重用密码相关联的安全问题。这个系统在验证过程中也执行一个安全密钥的交换,允许在会话过程中启用安全层(隐私和/或完整性保护)。这不要求信任的密钥服务器和证书基础结构,也不要求存储或管理任何长期的密钥。对比现有的挑战应答(challenge-response)技术,SRP 有安全性和部署的优势,在需要安全密码验证的时候是一个理想的替代方案。
完整的 RFC2945 规格位于:http://www.rfc-editor.org/rfc.html。关于 SRP 算法及其历史的其他信息,请访问 http://srp.stanford.edu/。
Diffie-Hellman 和 RSA 等算法被称为公共密钥交换算法。公共密钥交换算法的概念是你有两个密钥,一个是所有人都知道的公共密钥,另外一个是只有你自己知道的私有密钥。当某人想发送加密信息给你,他们会用公共密钥进行加密。你只能用自己的私有密钥对这些信息进行解密。和更多传统的基于共享密码的加密模式(要求发送者和接收者都知道共享的密码)相反,公共密钥算法不需要共享密码。

12.5.2. 配置安全远程密码协议

要在应用程序里使用安全远程密码(Secure Remote Password,SRP)协议,你首先可以创建一个实现 SRPVerifierStore 接口的 MBean。关于这个实现的信息位于 SRPVerifierStore 实现

过程 12.3. 集成现有的密码库

  1. 创建 hashed 密码信息库。

    如果密码已经以不可逆的 hashed 形式存储,对每个用户你都需要这么做。
    你可以实现 setUserVerifier(String, VerifierInfo) 为 noOp 方法,或者抛出表示这个库是只读的异常。
  2. 创建 SRPVerifierStore 接口。

    创建一个可以从你创建的库里获取 VerifierInfoSRPVerifierStore 接口实现。
    verifyUserChallenge(String, Object) 可用来集成现有的基于硬件令牌的模式(如 SafeWord 或 Radius)到 SRP 算法。这个接口方法只有当客户 SRPLoginModule 配置指定了 hasAuxChallenge 选项才被调用。
  3. 创建 JNDI MBean。

    创建一个开放 JNDI 可用的 SRPVerifierStore 接口的 MBean,并开放任何所需的可配置参数。
    默认的 org.jboss.security.srp.SRPVerifierStoreService 允许你实现它。你也可以用 SRPVerifierStore 的一个 Java 属性文件实现来实现这个 MBean。
SRPVerifierStore 实现

我们不推荐在产品环境里使用 SRPVerifierStore 接口的默认实现,因为它要求所有的密码

SRPVerifierStore 实现为给定用户提供了对 SRPVerifierStore.VerifierInfo 对象的访问。SRPService 在启动用户 SRP 会话来获取 SRP 算法所需参数时调用 getUserVerifier(String)

VerifierInfo 对象的元素

username
用来验证的用户 ID 或用户名
verifier
用户输入作为标识符凭证的单向哈希密码。org.jboss.security.Util 类包含了一个 calculateVerifier 方法,它执行密码的哈希算法。输出密码采用 H(salt | H(username | ':' | password)) 形式,其中 H 是 RFC2945 定义的 SHA 安全哈希功能。用户名将使用 UTF-8 编码从字符串转换为 byte[]。
salt
当数据库泄露时,随机数字用于增加对 verifier 密码数据库的 brute force 字典攻击的难度。当用户的现有明文密码进行哈希加密时,这个值应该根据一个强加密的随机数字算法生成。
g
SRP 算法的原始生成器。它可以是一个众所周知的固定参数而不用对每个用户进行设置。org.jboss.security.srp.SRPConf 工具类为 g 提供了几个设置,包括通过 SRPConf.getDefaultParams().g() 获得的合适的默认值。
N
SRP 算法的 safe-prime 系数。它可以是一个众所周知的固定参数而不用对每个用户进行设置。org.jboss.security.srp.SRPConf 工具类为 g 提供了几个设置,包括通过 SRPConf.getDefaultParams().g() 获得的合适的默认值。

例 12.13. SRPVerifierStore 接口

package org.jboss.security.srp;

import java.io.IOException;
import java.io.Serializable;
import java.security.KeyException;

public interface SRPVerifierStore
{
    public static class VerifierInfo implements Serializable
    {

        public String username;


        public byte[] salt;
        public byte[] g;
        public byte[] N;
    }
    

    public VerifierInfo getUserVerifier(String username)
        throws KeyException, IOException;

    public void setUserVerifier(String username, VerifierInfo info)
        throws IOException;


     public void verifyUserChallenge(String username, Object auxChallenge)
         throws SecurityException;
}

第 13 章 单点登录(SSO)

13.1. 关于 Web 应用程序的单点登录

概述

单点登录(Single Sign On,SSO)允许到某个资源的验证可以授权对其他资源的访问。

群集和非群集的 SSO

非群集的 SSO 限制分享授权信息至相同虚拟机上的应用程序。此外,它在主机出现故障时无弹性可言。而群集 SSO 数据可以在多个虚拟主机里的应用程序间共享,对失效切换有一定弹性。而且,群集 SSO 可以从负载平衡器接收请求。

SSO 是如何工作的

如果资源是未受保护的,用户根本不会被验证。如果用户访问了受保护的资源,用户将被验证。

在成功验证后,和用户相关联的角色将被存储并用于所有其他相关资源的授权。
如果用户从应用程序登出,或者应用程序在程序里使会话失效,所有保持的授权数据将被删除,这个过程将重新开始。
如果其他会话仍然有效,会话超时不会使 SSL 会话失效。

SSO 的限制

不能在第三方边界传播数据
SSO 只能在部署在 JBoss EAP 容器里的应用程序间使用。
只能用于容器管理的验证
你必须在应用程序 web.xml 里使用容器管理的授权元素,如 <login-config>
要求 cookie
SSO 通过浏览器 cookie 来保持且不支持 URL 重写。
区和安全域的限制
除非设置 requireReauthentication 参数为 true,配置相同 SSO 值的所有的 web 应用程序都应该共享 web.xml 里的相同的区(Realm)配置以及安全域。
你可以在 Host 元素或周围的 Engine 元素里嵌套 Realm 元素,但不能放在涉及的 web 应用程序里的 context.xml 元素里。
jboss-web.xml 里配置的 <security-domain> 必须在所有的 web 应用程序里一致。
所有的安全集成必须接受相同的凭证(如用户名和密码)。

13.2. 关于 Web 应用程序的群集单点登录

单点登录(Single Sign On,SSO)是指的用户通过单个 web 应用程序的验证,成功验证后被赋予对多个其他程序的授权。群集的 SSO 在群集缓存里存储了验证和授权信息。这允许了多个服务器上的应用程序共享信息,也使得对于其中的主机发生失效切换时更富有弹性。
SSO 配置被称为库(Valve)。库连接到在服务器或服务器组级别配置的安全域。你可以配置应该分享相同的缓存验证信息的应用程序来使用相同的库。这个配置是在应用程序的 jboss-web.xml 里完成的。
JBoss EAP 的 Web 子系统支持的一些常见 SSO 库包括:
  • Apache Tomcat ClusteredSingleSignOn
  • Apache Tomcat IDPWebBrowserSSOValve
  • SPNEGO-based SSO provided by PicketLink
根据库的类型,你可能需要在安全域里进行一些额外的配置来使它正常运转。

13.3. 选择正确的 SSO 实现

JBoss EAP 运行 Java EE 应用程序,这可以是 Web 应用程序、EJB 应用程序、Web 服务或其他类型的程序。单点登录(Single Sign On,SSO)允许你在这些应用程序间传播安全上下文和身份信息。根据机构的需要,你可以使用不同的 SSO 解决方案。你要使用的解决方案取决于你是否使用 Web 应用程序、EJB 应用程序还是 Web 服务;你的应用程序是否运行在相同的服务器、多个非群集服务器或多个群集服务器上;你是否需要集成到基于桌面的验证系统或者你只需要在应用程序之间来验证。
基于 Kerberos 的桌面单点登录

如果你的机构已经使用了基于 Kerberos 的验证和授权系统,如 Microsoft Active Directory,你可以使用相同的系统来透明地验证运行在 JBoss EAP 里的应用程序。

非群集 Web 应用程序单点登录

如果你需要在运行在相同服务器组或实例里的应用程序间传播安全信息,你可以使用非群集的 SSO。这只要在应用程序的 jboss-web.xml 描述符里配置库(valve)就可以了。

群集 Web 应用程序的单点登录

如果你需要在运行在跨多个 JBoss EAP 实例的群集环境里的应用程序间传播安全信息,你可以使用群集的 SSO 库(Valve)。这只要在应用程序的 jboss-web.xml 描述符里进行配置就可以了。

13.4. 在 Web 应用程序里使用单点登录

概述

单点登录(Single Sign On,SSO)是通过 web 和 Infinispan 子系统提供的。请使用下列步骤在 web 应用程序里配置 SSO。

前提条件

  • 你需要一个处理验证和授权的配置好的安全域。
  • 你需要 infinispan 子系统。对于受管域它位于 full-ha 配置集里,而对于独立服务器,你需要使用 standalone-full-ha.xml 配置。
  • webcache-container 和 SSO cache-container 都必须存在。配置文件示例已经包含了 web cache-container,而一些配置也已经包含了 SSO cache-container。请使用下列命令来检查并启用 SSO cache-container。请注意,这些命令修改了受管域的 full 配置集。对于独立服务器,你可以修改这些命令来使用其他的配置集,或者去掉命令行的 /profile=full 部分。

    例 13.1. 检查 web cache-container

    上面提及的配置集和配置在默认情况下包括 web cache-container。请使用下列命令来检验它的存在。如果你使用了不同的配置集,请用这个配置集的名称来替换 ha
    /profile=ha/subsystem=infinispan/cache-container=web/:read-resource(recursive=false,proxies=false,include-runtime=false,include-defaults=true)
    如果结果为 success,表示子系统存在。否则,你需要添加它。

    例 13.2. 添加 web cache-container

    请使用下列三个命令来启用你的配置里的 web cache-container。修改配置集的名称以及其他参数。这里的参数是用于默认配置的。
    /profile=ha/subsystem=infinispan/cache-container=web:add(aliases=["standard-session-cache"],default-cache="repl",module="org.jboss.as.clustering.web.infinispan")
    /profile=ha/subsystem=infinispan/cache-container=web/transport=TRANSPORT:add(lock-timeout=60000)
    /profile=ha/subsystem=infinispan/cache-container=web/replicated-cache=repl:add(mode="ASYNC",batching=true)

    例 13.3. 检查 SSO cache-container

    运行下列管理 CLI 命令:
    /profile=ha/subsystem=infinispan/cache-container=web/:read-resource(recursive=true,proxies=false,include-runtime=false,include-defaults=true)
    找到类似于 "sso" => { 的输出:
    如果没有找到则表示 SSO cache-container 没有在你的配置里出现。

    例 13.4. 添加 SSO cache-container

    /profile=ha/subsystem=infinispan/cache-container=web/replicated-cache=sso:add(mode="SYNC", batching=true)
  • 你需要配置 web 子系统来使用 SSO。下面的命令在名为 default-host 的虚拟服务器和 cookie 域 domain.com 上启用了 SSO。缓存名称是 sso,重新验证是禁用的。
    /profile=ha/subsystem=web/virtual-server=default-host/sso=configuration:add(cache-container="web",cache-name="sso",reauthenticate="false",domain="domain.com")
  • 你需要配置将共享 SSO 信息的每个应用程序以在 jboss-web.xml 里使用相同 <security-domain> 以及在 web.xml 里使用相同的 Realm 元素。
群集和非群集 SSO 阀之间的区别

群集 SSO 允许在不同的主机间共享验证信息,而非群集的 SSO 不允许。群集和非群集的 SSO 阀的配置方式相同,但群集 SSO 包含了 cacheConfigprocessExpiresIntervalmaxEmptyLife 参数,它们控制持久数据的群集复制。

例 13.5. 群集 SSO 配置示例

因为群集和非群集 SSO 配置是如此的相似,所以我们只列出了群集 SSO 的配置。这个例子使用了一个名为 tomcat 的安全域。
<jboss-web>
	<security-domain>tomcat</security-domain>
	<valve>
		<class-name>org.jboss.web.tomcat.service.sso.ClusteredSingleSignOn</class-name>
		<param>
			<param-name>maxEmptyLife</param-name>
			<param-value>900</param-value>
		</param>
	</valve>
</jboss-web>
		

表 13.1. SSO 配置选项

选项 描述
cookieDomain
用于 SSO cookie 的主机域。默认为 /。要允许 app1.xyz.comapp2.xyz.com 共享 SSO cookie,你可以设置 cookieDomain 为 xyz.com
maxEmptyLife
只适用于群集 SSO。没有活动会话的 SSO 阀可被请求所使用的最长时间(秒)。正值允许正确处理节点的关闭,如果它是唯一带有附加到阀的会话的节点的话。如果 maxEmptyLife 被设置为 0,当本地会话复制时阀将终止,但会从群集应用程序里备份会话以供其他群集节点使用。允许阀超过其受管会话的生命周期给了用户进行其他请求的时间,以便失效切换到不同的节点,从而可以激活会话的备份。它默认为 1800 秒(30 分钟)。
processExpiresInterval
只适用于群集 SSO。阀寻找和使已超过 MaxEmptyLife 的 SSO 实例失效的最长时间(秒)。默认为 60 秒(1 分钟)。
requiresReauthentication
如果为 true,每个请求都使用缓存的凭证来重新验证安全域。如果为 false(默认值),对于这个阀来说,有效的 SSO cookie 就足够验证每个新的请求了。
使会话失效

应用程序可以通过调用 javax.servlet.http.HttpSession.invalidate() 方法在程序里使会话失效。

13.5. 关于 Kerberos

Kerberos 是一个用于客户/服务器应用程序的网络验证协议。它允许以安全的方式、使用密钥对称加密跨非安全网络进行验证。
Kerberos 使用称为票证(ticket)的加密令牌。要使用安全服务,你需要从运行在网络上某个服务器上的票证授予服务(Ticket Granting Service,TGS)获得一个票证。在获得票证后,你可以向网络里运行的验证服务(Authentication Service,AS)请求一个服务票证(Service Ticket,ST)。然后你可以使用这个 ST 来验证你要使用的服务。TGS 和 AS 都运行在名为密钥分发中心(Key Distribution Center,KDC)的一个封装服务的内部。
Kerberos 的目的是用于客户/服务器环境,而它很少运行在瘦客户端环境或 Web 应用程序里。然而,许多机构已经将其用于桌面验证,且愿意重用现有的系统而不是为 Web 应用程序创建另外一个验证系统。Kerberos 是 Microsoft Active Directory 的一个固有部分,它也用在许多红帽企业版 Linux 环境里。

13.6. 关于 SPNEGO

简单和受保护的 GSSAPI 协商机制(Simple and Protected GSS_API Negotiation Mechanism,SPNEGO)提供了一个扩展基于 Kerberos 的单点登录(Single Sign On,SSO)环境的机制。
当客户端上的应用程序(如浏览器)试图访问 web 服务器上的受保护页面时,服务器会回应需要授权。然后应用程序将从 Kerberos 密钥分发中心(Kerberos Key Distribution Center ,KDC)请求一个票证。获得这个票证后,应用程序将其包裹在一个 SPNEGO 格式的请求里,并通过浏览器送回 Web 应用程序。运行部署的 web 应用程序的 web 容器将解开这个请求并验证这个票证,并在成功验证后授予访问权限。
SPNEGO 可以和所有类型的 Kerberos 提供者一起使用,包括红帽企业版 Linux 包含的 Kerberos 服务以及作为 Microsoft Active Directory 的固有部分的 Kerberos 服务器。

13.7. 关于 Microsoft Active Directory

Microsoft Active Directory 是微软公司开放的一个验证 Microsoft Windows 域里的用户和主机的目录服务。它是 Microsoft Windows 服务器的一部分。Microsoft Windows 服务器里的主机被称为域控制器。运行 Samba 服务的红帽企业版 Linux 服务器也可以充当这种类型的网络的域控制器。
Active Directory 依赖于三种一起工作的核心技术:
  • 轻量级目录访问协议(Lightweight Directory Access Protocol,LDAP),存储用户、主机、密码和其他资源的信息。
  • Kerberos,提供网络上的安全验证。
  • 域名服务(Domain Name Service,DNS)提供 IP 地址和网络上主机和其他设备的名称之间的映射。

13.8. 为 Web 应用程序配置 Kerberos 或 Microsoft Active Directory 桌面单点登录

简介

要使用机构现有的基于 Kerberos 的验证和授权体系(如 Microsoft Active Directory)来验证你的 Web 或 EJB 应用程序,你可以使用 JBoss EAP 6 里内嵌的 JBoss Negotation 功能。如果你正确配置了你的 web 应用程序,成功的桌面或网络登录就足够透明地验证你的 we 应用程序,而不需要额外的登录提示。

与之前版本的不同之处

JBoss EAP 6 和之前的版本有着一些值得注意的区别:

  • 对于受管域的每个配置集或每个独立服务器来说,安全域都是集中配置的。它们不是部署本身的一部分。部署应该使用的安全域是在部署的 jboss-web.xmljboss-ejb3.xml 文件里命名的。
  • 安全属性是作为安全域也是中央配置的一部分配置的。它们不是部署的一部分。
  • 你无法再覆盖作为部署一部分的验证。然而,你可以添加一个 NegotiationAuthenticator 阀到 jboss-web.xml 描述符以实现相同的效果。这个值仍然要求在 web.xml 里定义 <security-constraint><login-config> 元素。它们用于确定哪些资源是安全的。然而,所选的 auth-method 将被 jboss-web.xml 里的 NegotiationAuthenticator 阀所替代。
  • 安全域里的 CODE 属性现在使用一个简单的名称而不是全限定类名。下表展示了对 JBoss Negotiation 的类的映射。

表 13.2. 登录模块代码和类名

简单名称 类名 目的
Kerberos com.sun.security.auth.module.Krb5LoginModule Kerberos 登录模块
SPNEGO org.jboss.security.negotiation.spnego.SPNEGOLoginModule 启用 web 应用程序向 Kerberos 验证服务器验证的机制。
AdvancedLdap org.jboss.security.negotiation.AdvancedLdapLoginModule 和 LDAP 服务器而不是 Microsoft Active Directory 一起使用。
AdvancedAdLdap org.jboss.security.negotiation.AdvancedADLoginModule 和 Microsoft Active Directory LDAP 服务器一起使用。
Jboss Negotiation Toolkit

The JBoss Negotiation Toolkit is a debugging tool which is available for download from https://community.jboss.org/servlet/JiveServlet/download/16876-2-34629/jboss-negotiation-toolkit.war. It is provided as an extra tool to help you to debug and test the authentication mechanisms before introducing your application into production. It is an unsupported tool, but is considered to be very helpful, as SPNEGO can be difficult to configure for web applications.

过程 13.1. 设置 Web 或 EJB 应用程序的 SSO 验证

  1. 配置一个安全域来代表服务器的身份。如果需要则设置系统属性。

    第一个安全域向目录服务验证容器自身。它需要使用一个接受某种静态登录机制的登录模块,因为这没有涉及真正的用户。这个例子使用了一个静态 principal 并引用了一个包含凭证的 Keytab 文件。
    下面通过 XML 片段进行阐述,但你应该使用管理控制台或 CLI 来配置你的安全域。
    <security-domain name="host" cache-type="default">
       <authentication>
          <login-module code="Kerberos" flag="required">
             <module-option name="storeKey" value="true"/>
             <module-option name="useKeyTab" value="true"/>
             <module-option name="principal" value="host/testserver@MY_REALM"/>
             <module-option name="keyTab" value="/home/username/service.keytab"/>
             <module-option name="doNotPrompt" value="true"/>
             <module-option name="debug" value="false"/>
          </login-module>
       </authentication>
    </security-domain>		
    		
    
    
  2. 配置第二个安全域来保护 web 或 EJB 应用程序。如果有必要请设置系统属性。

    第二个安全域用来向 Kerberos 或 SPNEGO 验证服务器验证单独的用户。你需要至少一个登录模块来验证用户,另外一个则搜索适用于用户的角色。下面的 XML 片段展示了一个 SPNEGO 安全域示例。它包含了一个授权模块来映射角色和单独的用户。你也可以使用一个搜索验证服务器自己的角色的模块。
    <security-domain name="SPNEGO" cache-type="default">
       <authentication>
          <!-- Check the username and password -->
          <login-module code="SPNEGO"  flag="requisite">
             <module-option name="password-stacking" value="useFirstPass"/>
             <module-option name="serverSecurityDomain" value="host"/>
          </login-module>
          <!-- Search for roles -->
          <login-module code="UserRoles" flag="required">
             <module-option name="password-stacking" value="useFirstPass" />
             <module-option name="usersProperties" value="spnego-users.properties" />
             <module-option name="rolesProperties" value="spnego-roles.properties" />
          </login-module> 
       </authentication>
    </security-domain>		
    		
    
    
  3. web.xml 里指定 security-constraint 和 login-config。

    web.xml 描述符包含了安全约束和登录配置的信息。下面是两者的示例。
    <security-constraint>
       <display-name>Security Constraint on Conversation</display-name>
       <web-resource-collection>
          <web-resource-name>examplesWebApp</web-resource-name>
          <url-pattern>/*</url-pattern>
       </web-resource-collection>
       <auth-constraint>
       <role-name>RequiredRole</role-name>
       </auth-constraint>
    </security-constraint>
    
    <login-config>
       <auth-method>SPNEGO</auth-method>
       <realm-name>SPNEGO</realm-name>
    </login-config>
     
    <security-role>
       <description> role required to log in to the Application</description>
       <role-name>RequiredRole</role-name>
    </security-role>		
    		
    
    
  4. jboss-web.xml 描述符里指定安全域和其他设置。

    在部署的 jboss-web.xml 描述符里指定客户端安全域(这个例子里的第二个)的名称,指引你的应用程序来使用这个安全域。
    你无法再直接覆盖验证。相反,如果需要的话你可以在 jboss-web.xml 里添加 NegotiationAuthenticator 为阀。<jacc-star-role-allow> 允许你使用星号(*)来匹配多个角色名,它是可选的。
    <jboss-web>
       <security-domain>java:/jaas/SPNEGO</security-domain>
       <valve>
          <class-name>org.jboss.security.negotiation.NegotiationAuthenticator</class-name>
       </valve>
       <jacc-star-role-allow>true</jacc-star-role-allow>
    </jboss-web>		
    		
    
    
  5. 添加一个依赖关系到应用程序的 MANIFEST.MF 以定位 Negotiation 类。

    Web 应用程序需要在部署的 META-INF/MANIFEST.MF manifest 里添加一个 org.jboss.security.negotiation 类的依赖关系,从而定义 JBoss Negotiation 类。下面是一个具有正确格式的条目。
    Manifest-Version: 1.0
    Build-Jdk: 1.6.0_24
    Dependencies: org.jboss.security.negotiation
    
结果

你的 Web 应用程序通过 Kerberos、Microsoft Active Directory 或其他兼容 SPNEGO 的目录服务接受和验证凭证。如果用户在一个已经登录至目录服务的系统里运行这个应用程序,而要求的角色已经应用到用户,那么这个 Web 应用程序将不会再提示进行验证,从而实现了 SSO 功能。

第 14 章 应用程序里基于角色的安全性

14.2. 关于安全性审计

安全性审计包括触发事件,如在响应安全子系统里发生的事件而写入日志。审计机制和验证、授权、安全性映射细节都是作为安全域的一部分来配置的。
审计使用提供者模块。你可以使用内含的模块,或者实现自己的模块。

14.3. 关于安全性映射

安全性映射允许你在验证或授权发生后但在信息传递给应用程序之前合并验证和授权信息。其中一个例子是使用用于验证的 X509 证书,然后从证书转换 principal 为你的应用程序可以显示的逻辑名称。
你可以映射 principal(验证)、角色(授权)或凭证(非 principal 或角色的属性)。
角色映射用于在验证后对主题添加、替换或删除角色。
Principal 映射用于在验证后修改 principal。
属性映射用来从外部系统转换属性以供应用程序使用,反之亦然。

14.4. 关于安全性扩展架构

JBoss EAP 的安全性扩展架构(Security Extension Architecture)由三个部分组成。这三个部分连接你的应用程序到底层的安全性架构,如 LDAP、Kerberos 或其他外部系统。
JAAS

这个架构的第一部分是 JAAS API。JAAS 是一个可插拔的框架,它为安全性架构和应用程序间提供了一个抽象层。

JAAS 里的主要实现是 org.jboss.security.plugins.JaasSecurityManager,它实现了 AuthenticationManager RealmMapping 接口。基于对应的组件部署描述符的 <security-domain> 元素,JaasSecurityManager 集成到了 EJB 和 web 容器层。
关于 JAAS 的更多信息,请参考 第 16.3 节 “Java 认证和授权服务(JAAS)”
JaasSecurityManagerService MBean

JaasSecurityManagerService MBean 服务管理安全性管理者。虽然它的名字以 Jaas 开始,但它处理的安全性管理者并不需要在其实现里使用 JAAS。这个名字反映的事实是,默认的安全性管理者实现是 JaasSecurityManager

JaasSecurityManagerService 的主要角色是具体化安全性管理者实现。你可以通过对 AuthenticationManager RealmMapping 接口的其他实现来修改安全性管理者实现。
JaasSecurityManagerService 的第二个基础角色是提供一个 JNDI javax.naming.spi.ObjectFactory 实现以允许对 JNDI 名称和安全性管理者实现间绑定的简单的无代码管理。要启用安全性,可以通过 <security-domain> 部署描述符元素简化安全性管理者实现的 JNDI 名称。
当你指定一个 JNDI 名称时,object-binding 必须已经存在。要简化 JNDI 名称和安全性管理者间绑定的设置, JaasSecurityManagerService 绑定了一个 next naming system reference,提名自己为 java:/jaas 下的 JNDI ObjectFactory。这允许了 java:/jaas/XYZ 形式的命名来作为 <security-domain> 元素的值,且 XYZ 安全域的安全管理者实例按需要创建,这是通过创建一个 SecurityManagerClassName 属性指定的类的实例,并使用采用安全域的名称的构造器来完成的。

注意

你不需要在你的部署描述符里包含 java:/jaas 前缀。虽然为了向后的兼容性,你可以这样做,但它会被忽略。
JaasSecurityDomain MBean

org.jboss.security.plugins.JaasSecurityDomain JaasSecurityManager 的一个扩展,它添加了 KeyStore KeyManagerFactory TrustManagerFactory 以支持 SSL 和其他加密的用例。

进一步的信息

关于安全性架构的更多信息以及实践示例,请参考 第 14.5 节 “关于 Java 认证和授权服务(JAAS)”

14.5. 关于 Java 认证和授权服务(JAAS)

JBoss EAP 6 的安全性架构由安全性配置子系统、包含在几个配置文件里的应用程序专有的安全性配置以及实现为 MBean 的 JAAS 安全性管理者组成。
与、服务器组和服务器专有的配置

服务器组(受管域里)和服务器(独立服务器里)包含了安全域的配置。安全域包括合并验证、授权、映射和审计模块的信息以及配置细节。应用程序在 jboss-web.xml 里通过名字指定它需要哪个安全域。

应用程序专有的配置

应用程序专有的配置出现在一个或多个下列的四个文件里。

表 14.1. 应用程序专有的配置文件

文件 描述
ejb-jar.xml
EJB 应用程序的部署描述符位于 META-INF 目录。请使用 ejb-jar.xml 来指定角色并映射到应用程序级别的 principal 上。你也可以将特定的方法和类限制到某些角色。这也可以用于其他和安全性无关的 EJB 专有的配置。
web.xml
Java EE web 应用程序的部署描述符。请使用 web.xml 来声明应用程序用于验证和授权的安全域,以及资源和传输约束,如限制允许哪种类型的 HTTP 请求。你也可以在这个文件里配置简单的基于 web 的验证。它也可以用于和安全性无关的其他应用程序专有的配置。
jboss-ejb3.xml
包含对 ejb-jar.xml 描述符的 JBoss 专有的扩展内容。
jboss-web.xml
包含对 web.xml 描述符的 JBoss 专有的扩展内容。

注意

ejb-jar.xmlweb.xml 是在 Java EE 规格里定义的。jboss-ejb3.xml 提供了对 ejb-jar.xml 的 JBoss 专有的扩展,而 jboss-web.xml 提供了对 web.xml 的 JBoss 专有的扩展。
JAAS 安全性管理者 MBean

Java 验证和授权服务(Java Authentication and Authorization Service,JAAS)是一个 Java 应用程序里用户级别安全性的框架,它使用可插拔的验证模块(Pluggable Authentication Module,PAM)。它被集成到 Java Runtime Environment(JRE)里。在 JBoss EAP 里,容器端的组件是 org.jboss.security.plugins.JaasSecurityManager MBean。它提供了对 AuthenticationManagerRealmMapping 接口的默认实现。

JaasSecurityManager MBean 根据 EJB 或 web 部署描述符文件里指定的安全域集成到 EJB 和 web 容器层里。当部署一个应用程序时,容器将部署描述符里指定的安全域和容器的安全性管理者实例相关联。按照服务器组或独立服务器上所配置的,安全性管理者将执行安全域的配置。
在客户和容器间使用 JAAS 的交互流程

JaasSecurityManager 使用 JAAS 软件包来实现 AuthenticationManager 和RealmMapping 接口行为。特别是,它的行为源于 JaasSecurityManager 已经分配的安全域里配置的登录模块实例的执行。登录模块实现了安全域的 principal 验证和角色映射行为。通过插入域的不同的登录模块配置,你可以在不同的安全域里使用 JaasSecurityManager。

为了说明 JaasSecurityManager 是如何使用 JAAS 验证过程的,下面的步骤概述了实现了 EJBHome 方法的客户端方法调用。EJB 已经部署在服务器里,它的 EJBHome 接口方法已经使用 ejb-jar.xml 里的 <method-permission> 元素进行了保护。它使用 jboss-ejb3.xml 里的 <security-domain> 元素指定的 jwdomain 安全域。下面的图表展示了这些步骤,我们在后面将进行解释。
EJB 的验证步骤

图 14.1. 安全的 EJB 方法调用的步骤

  1. 客户端执行了一个 JAAS 登录来建立用于验证的 principal 和凭证。这在图表里被标注为 Client Side Login。它也可以通过 JNDI 来执行。
    要执行 JAAS 登录,你可以创建一个 LoginContext 实例并传入要使用的配置的名称。在这里,配置的名称是 other。这个一次性的登录将登录 principal 以及凭证和随后的所有 EJB 调用相关联。这个过程并不需要验证用户。客户端登录的性质取决于客户使用的登录模块配置。在这个例子里,other 客户端登录配置条目使用了 ClientLoginModule 登录模块。这个模块绑定用户名和密码到 EJB 调用层以供服务器上之后的验证。客户端标识符不在客户端进行验证。
  2. 客户端包含 EJBHome 方法并在服务器上调用它。这个调用包含客户端传入的方法参数,以及来自客户端 JAAS 登录的用户标识符和凭证。
  3. 在服务器上,安全性拦截器验证调用方法的用户。这涉及了另外一个 JAAS 登录。
  4. 安全域确定了登录模块的选择。安全域的名字作为登录配置条目名称被传入 LoginContext 构造器。EJB 安全域是 jwdomain。如果 JAAS 验证成功,JAAS 主题将被创建。JAAS 主题将包括一个 PrincipalSet,它有如下内容:
    • 一个 java.security.Principal 实例,它对应部署安全环境的客户端标识符。
    • 名为 Rolesjava.security.acl.Group,它包含用户应用程序域的角色名称。类型为 org.jboss.security.SimplePrincipal 的对象代表角色名称。这些对象按照 ejb-jar.xml 里的约束和 EJBContext.isCallerInRole(String) 方法实现检验对 EJB 方法的访问。
    • 名为 CallerPrincipal 的可选 java.security.acl.Group,它包含一个对应应用程序域的调用者的标识符的 org.jboss.security.SimplePrincipal。CallerPrincipal 组成员是 EJBContext.getCallerPrincipal() 方法返回的值。这个映射允许可操作安全环境里的 Principal 映射到应用程序已知的 Principal。在缺乏 CallerPrincipal 映射的情况下,可操作的 principal 和应用程序域 principal 是相同的。
  5. 服务器检验调用 EJB 方法的用户是否具有权限。执行这个授权涉及两个步骤:
    • 获取被允许从 EJB 容器访问 EJB 方法的角色的名称。这些角色名称由 ejb-jar.xml 的 <role-name> 元素和包含被调用方法的 <method-permission> 元素来确定。
    • 如果没有分配角色,或者方法是在一个 exclude-list 元素里指定的,那对这个方法的访问将被拒绝。否则,安全性拦截器将在安全管理者上调用 doesUserHaveRole 方法以检查调用者是否具有分配的角色名称。这个方法迭代角色名称并检查已验证的用户的 Subject Roles 组是否包含具有分配的角色名称的 SimplePrincipal。如果任何角色名称是 Roles 组的成员,访问将被允许。而如果任何一个角色名称都不是成员,访问将被拒绝。
    • 如果 EJB 使用一个自定义的安全代理,那这个方法调用将被委托给这个代理。如果安全大力拒绝了对调用者的访问,它将抛出 java.lang.SecurityException。否则,对 EJB 方法的访问将被允许,且方法调用将传递给下一个容器拦截器。SecurityProxyInterceptor 处理这个检查且这个拦截器没有被显示。
    • 对于 web 连接请求,web 服务器将检查匹配请求的资源和被访问的 HTTP 方法的、 web.xml 里定义的安全性约束。
      如果存在对于这个请求的约束,web 服务器将调用 JaasSecurityManager 来执行 principal 验证,从而确保用户角色和 principal 对象相关联。

14.6. 在应用程序里使用安全域

概述

要在应用程序里使用安全域,首先你必须通过服务器配置文件或应用程序的描述符文件配置安全域。然后你必须添加必要的注解到使用安全域的 EJB。这个主题涵盖了在应用程序里使用安全域所需的步骤。

过程 14.1. 配置你的应用程序以使用安全域

  1. 定义安全域

    你可以在服务器的配置文件或应用程序的描述符里定义安全域。
    • 在服务器的配置文件里配置安全域

      安全域是在服务器配置文件的 security 子系统里配置的。如果 JBoss EAP 实例运行在受管域里,配置文件应该是 domain/configuration/domain.xml。如果是独立服务器,则是 standalone/configuration/standalone.xml 文件。
      otherjboss-web-policyjboss-ejb-policy 都是 JBoss EAP 6 里默认提供的安全域。下面的 XML 示例是从服务器配置文件的 security 子系统里复制的。
      <subsystem xmlns="urn:jboss:domain:security:1.2">
          <security-domains>
              <security-domain name="other" cache-type="default">
                  <authentication>
                      <login-module code="Remoting" flag="optional">
                          <module-option name="password-stacking" value="useFirstPass"/>
                      </login-module>
                      <login-module code="RealmDirect" flag="required">
                          <module-option name="password-stacking" value="useFirstPass"/>
                      </login-module>
                  </authentication>
              </security-domain>
              <security-domain name="jboss-web-policy" cache-type="default">
                  <authorization>
                      <policy-module code="Delegating" flag="required"/>
                  </authorization>
              </security-domain>
              <security-domain name="jboss-ejb-policy" cache-type="default">
                  <authorization>
                      <policy-module code="Delegating" flag="required"/>
                  </authorization>
              </security-domain>
          </security-domains>
      </subsystem>
      
      
      你可以按需要用管理控制台或 CLI 配置其他的安全域。
    • 在应用程序的描述符文件里配置安全域

      安全域是在应用程序的 WEB-INF/jboss-web.xml 文件里的 <jboss-web> 元素的<security-domain> 子元素里指定的。下面的例子配置了一个名为 my-domain 的安全域。
      <jboss-web>
          <security-domain>my-domain</security-domain>
      </jboss-web>        
              
      
      
      这只是你可以在 WEB-INF/jboss-web.xml 描述符里指定的许多设置中的一个。
  2. 在 EJB 里添加必需的注解

    你可以用 @SecurityDomain@RolesAllowed 注解在 EJB 里配置安全性。下面的 EJB 代码示例限制了具有 guest 角色的用户对 other 安全域的访问。
    package example.ejb3;
    
    import java.security.Principal;
    
    import javax.annotation.Resource;
    import javax.annotation.security.RolesAllowed;
    import javax.ejb.SessionContext;
    import javax.ejb.Stateless;
    
    import org.jboss.ejb3.annotation.SecurityDomain;
    
    /**
     * Simple secured EJB using EJB security annotations
     * Allow access to "other" security domain by users in a "guest" role.
     */
    @Stateless
    @RolesAllowed({ "guest" })
    @SecurityDomain("other")
    public class SecuredEJB {
    
       // Inject the Session Context
       @Resource
       private SessionContext ctx;
    
       /**
        * Secured EJB method using security annotations
        */
       public String getSecurityInfo() {
          // Session context injected using the resource annotation
          Principal principal = ctx.getCallerPrincipal();
          return principal.toString();
       }
    }
    
    关于更多的代码示例,请参考 JBoss EAP 6 Quickstarts 集里的 ejb-security quickstart,你可以在红帽的客户门户找到这些例子。

14.7. 在 Servlet 里使用基于角色的安全性

要在 servlet 里添加安全性,你可以将每个 servlet 映射到 URL 模式,并在需要设置安全性的 URL 模式上创建安全性约束。这些安全性约束限制对 URL 的访问的角色。其验证和授权是由 WAR 的 jboss-web.xml 里指定的安全域处理的。
前提条件

在你在 servlet 里使用基于角色的安全性之前,用来验证和授权访问的安全域需要在 JBoss EAP 容器里进行配置。

过程 14.2. 在 Servlet 里添加基于角色的安全性

  1. 在 servlet 和 URL 模式间添加映射。

    使用 web.xml 里的 <servlet-mapping> 元素来映射单独的 servlet 和 URL 模式。下面的例子映射名为 DisplayOpResult 的 servlet 到 URL 模式 /DisplayOpResult
    <servlet-mapping>
        <servlet-name>DisplayOpResult</servlet-name>
        <url-pattern>/DisplayOpResult</url-pattern>
    </servlet-mapping>		
    			
    
    
  2. 添加安全约束到 URL 模式。

    要映射 URL 模式到安全约束,可以使用 <security-constraint>。下面的例子约束了具有角色 eap_admin 的 principal 对 URL 模式 /DisplayOpResult 的访问。这个角色需要出现在安全域里。
    <security-constraint>
    	<display-name>Restrict access to role eap_admin</display-name>
    	<web-resource-collection>
    		<web-resource-name>Restrict access to role eap_admin</web-resource-name>
    		<url-pattern>/DisplayOpResult/*</url-pattern>
    	</web-resource-collection>
    	<auth-constraint>
    		<role-name>eap_admin</role-name>
    	</auth-constraint>	
    	<security-role>
    		<role-name>eap_admin</role-name>
    	</security-role>
    </security-constraint>		
    			
    
    
  3. 在 WAR 的 jboss-web.xml 里指定安全域

    添加安全域到 WAR 的 jboss-web.xml 以连接 servlet 到配置好的安全域,它知道如何根据安区性约束验证和授权 principal。下面的例子使用名为 acme_domain 的安全域。
    <jboss-web>
    	...
    	<security-domain>acme_domain</security-domain>
    	...
    </jboss-web>
    			
    
    

14.8. 在应用程序里使用第三方的验证系统

你可以在 JBoss EAP 里集成第三方安全系统。这些系统通常是基于令牌的。外部的系统执行验证并将一个令牌通过请求头部回传给 Web 应用程序。这通常被称为周边验证(perimeter authentication)。要在应用程序里配置周边验证,请添加一个自定义验证阀。如果第三方提供者提供了验证阀,请确保将其放入 classpath 并遵循下面例子里的步骤,以及参考第三方验证模块的文档。

注意

JBoss EAP 6 已经改变了配置阀的位置。它不再位于 context.xml 里,它现在直接在 jboss-web.xml 描述符里进行配置。context.xml 现在已被忽略了。

例 14.1. 基本的验证阀

<jboss-web>
  <valve>
    <class-name>org.jboss.security.negotiation.NegotiationAuthenticator</class-name>
  </valve>
</jboss-web>

这个阀用于基于 Kerberos 的 SSO。它也展示了为 web 应用程序指定第三方验证器的多数简单模式。

例 14.2. 带有头部属性集的自定义阀

<jboss-web>
  <valve>
    <class-name>org.jboss.web.tomcat.security.GenericHeaderAuthenticator</class-name>
    <param>
      <param-name>httpHeaderForSSOAuth</param-name>
      <param-value>sm_ssoid,ct-remote-user,HTTP_OBLIX_UID</param-value>
    </param>
    <param>
      <param-name>sessionCookieForSSOAuth</param-name>
      <param-value>SMSESSION,CTSESSION,ObSSOCookie</param-value>
    </param>
  </valve>
</jboss-web>

这个例子展示了如何在阀上设置自定义的属性。验证器检查头部 ID 和会话密钥的存在,并作为用户名和密码值传入驱动安全层的 JAAS 框架。你需要一个自定义的 JAAS 登录模块来处理用户名和密码并用正确的角色传播主题。而如果没有对应配置值的头部值,普通的基于表单的验证模式将被使用。
编写自定义验证器

编写自定义的验证器超出了本文档的范畴。然而,下面的 Java 代码可以作为例子参考。

例 14.3. GenericHeaderAuthenticator.java

/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2006, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
 
package org.jboss.web.tomcat.security;

import java.io.IOException;
import java.security.Principal;
import java.util.StringTokenizer;

import javax.management.JMException;
import javax.management.ObjectName;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.catalina.Realm;
import org.apache.catalina.Session;
import org.apache.catalina.authenticator.Constants;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.deploy.LoginConfig;
import org.jboss.logging.Logger;

import org.jboss.as.web.security.ExtendedFormAuthenticator;

/**
 * JBAS-2283: Provide custom header based authentication support
 * 
 * Header Authenticator that deals with userid from the request header Requires
 * two attributes configured on the Tomcat Service - one for the http header
 * denoting the authenticated identity and the other is the SESSION cookie
 * 
 * @author <a href="mailto:Anil.Saldhana@jboss.org">Anil Saldhana</a>
 * @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a>
 * @version $Revision$
 * @since Sep 11, 2006
 */
public class GenericHeaderAuthenticator extends ExtendedFormAuthenticator {
  protected static Logger log = Logger
      .getLogger(GenericHeaderAuthenticator.class);

  protected boolean trace = log.isTraceEnabled();

  // JBAS-4804: GenericHeaderAuthenticator injection of ssoid and
  // sessioncookie name.
  private String httpHeaderForSSOAuth = null;

  private String sessionCookieForSSOAuth = null;

  /**
   * <p>
   * Obtain the value of the <code>httpHeaderForSSOAuth</code> attribute. This
   * attribute is used to indicate the request header ids that have to be
   * checked in order to retrieve the SSO identity set by a third party
   * security system.
   * </p>
   * 
   * @return a <code>String</code> containing the value of the
   *         <code>httpHeaderForSSOAuth</code> attribute.
   */
  public String getHttpHeaderForSSOAuth() {
    return httpHeaderForSSOAuth;
  }

  /**
   * <p>
   * Set the value of the <code>httpHeaderForSSOAuth</code> attribute. This
   * attribute is used to indicate the request header ids that have to be
   * checked in order to retrieve the SSO identity set by a third party
   * security system.
   * </p>
   * 
   * @param httpHeaderForSSOAuth
   *            a <code>String</code> containing the value of the
   *            <code>httpHeaderForSSOAuth</code> attribute.
   */
  public void setHttpHeaderForSSOAuth(String httpHeaderForSSOAuth) {
    this.httpHeaderForSSOAuth = httpHeaderForSSOAuth;
  }

  /**
   * <p>
   * Obtain the value of the <code>sessionCookieForSSOAuth</code> attribute.
   * This attribute is used to indicate the names of the SSO cookies that may
   * be present in the request object.
   * </p>
   * 
   * @return a <code>String</code> containing the names (separated by a
   *         <code>','</code>) of the SSO cookies that may have been set by a
   *         third party security system in the request.
   */
  public String getSessionCookieForSSOAuth() {
    return sessionCookieForSSOAuth;
  }

  /**
   * <p>
   * Set the value of the <code>sessionCookieForSSOAuth</code> attribute. This
   * attribute is used to indicate the names of the SSO cookies that may be
   * present in the request object.
   * </p>
   * 
   * @param sessionCookieForSSOAuth
   *            a <code>String</code> containing the names (separated by a
   *            <code>','</code>) of the SSO cookies that may have been set by
   *            a third party security system in the request.
   */
  public void setSessionCookieForSSOAuth(String sessionCookieForSSOAuth) {
    this.sessionCookieForSSOAuth = sessionCookieForSSOAuth;
  }

  /**
   * <p>
   * Creates an instance of <code>GenericHeaderAuthenticator</code>.
   * </p>
   */
  public GenericHeaderAuthenticator() {
    super();
  }

  public boolean authenticate(Request request, HttpServletResponse response,
      LoginConfig config) throws IOException {
    log.trace("Authenticating user");

    Principal principal = request.getUserPrincipal();
    if (principal != null) {
      if (trace)
        log.trace("Already authenticated '" + principal.getName() + "'");
      return true;
    }

    Realm realm = context.getRealm();
    Session session = request.getSessionInternal(true);

    String username = getUserId(request);
    String password = getSessionCookie(request);

    // Check if there is sso id as well as sessionkey
    if (username == null || password == null) {
      log.trace("Username is null or password(sessionkey) is null:fallback to form auth");
      return super.authenticate(request, response, config);
    }
    principal = realm.authenticate(username, password);

    if (principal == null) {
      forwardToErrorPage(request, response, config);
      return false;
    }

    session.setNote(Constants.SESS_USERNAME_NOTE, username);
    session.setNote(Constants.SESS_PASSWORD_NOTE, password);
    request.setUserPrincipal(principal);

    register(request, response, principal, HttpServletRequest.FORM_AUTH,
        username, password);
    return true;
  }

  /**
   * Get the username from the request header
   * 
   * @param request
   * @return
   */
  protected String getUserId(Request request) {
    String ssoid = null;
    // We can have a comma-separated ids
    String ids = "";
    try {
      ids = this.getIdentityHeaderId();
    } catch (JMException e) {
      if (trace)
        log.trace("getUserId exception", e);
    }
    if (ids == null || ids.length() == 0)
      throw new IllegalStateException(
          "Http headers configuration in tomcat service missing");

    StringTokenizer st = new StringTokenizer(ids, ",");
    while (st.hasMoreTokens()) {
      ssoid = request.getHeader(st.nextToken());
      if (ssoid != null)
        break;
    }
    if (trace)
      log.trace("SSOID-" + ssoid);
    return ssoid;
  }

  /**
   * Obtain the session cookie from the request
   * 
   * @param request
   * @return
   */
  protected String getSessionCookie(Request request) {
    Cookie[] cookies = request.getCookies();
    log.trace("Cookies:" + cookies);
    int numCookies = cookies != null ? cookies.length : 0;

    // We can have comma-separated ids
    String ids = "";
    try {
      ids = this.getSessionCookieId();
      log.trace("Session Cookie Ids=" + ids);
    } catch (JMException e) {
      if (trace)
        log.trace("checkSessionCookie exception", e);
    }
    if (ids == null || ids.length() == 0)
      throw new IllegalStateException(
          "Session cookies configuration in tomcat service missing");

    StringTokenizer st = new StringTokenizer(ids, ",");
    while (st.hasMoreTokens()) {
      String cookieToken = st.nextToken();
      String val = getCookieValue(cookies, numCookies, cookieToken);
      if (val != null)
        return val;
    }
    if (trace)
      log.trace("Session Cookie not found");
    return null;
  }

  /**
   * Get the configured header identity id in the tomcat service
   * 
   * @return
   * @throws JMException
   */
  protected String getIdentityHeaderId() throws JMException {
    if (this.httpHeaderForSSOAuth != null)
      return this.httpHeaderForSSOAuth;
    return (String) mserver.getAttribute(new ObjectName(
        "jboss.web:service=WebServer"), "HttpHeaderForSSOAuth");
  }

  /**
   * Get the configured session cookie id in the tomcat service
   * 
   * @return
   * @throws JMException
   */
  protected String getSessionCookieId() throws JMException {
    if (this.sessionCookieForSSOAuth != null)
      return this.sessionCookieForSSOAuth;
    return (String) mserver.getAttribute(new ObjectName(
        "jboss.web:service=WebServer"), "SessionCookieForSSOAuth");
  }

  /**
   * Get the value of a cookie if the name matches the token
   * 
   * @param cookies
   *            array of cookies
   * @param numCookies
   *            number of cookies in the array
   * @param token
   *            Key
   * @return value of cookie
   */
  protected String getCookieValue(Cookie[] cookies, int numCookies,
      String token) {
    for (int i = 0; i < numCookies; i++) {
      Cookie cookie = cookies[i];
      log.trace("Matching cookieToken:" + token + " with cookie name="
          + cookie.getName());
      if (token.equals(cookie.getName())) {
        if (trace)
          log.trace("Cookie-" + token + " value=" + cookie.getValue());
        return cookie.getValue();
      }
    }
    return null;
  }
}

第 15 章 移植

15.1. 应用程序安全性的修改

配置基本验证的安全性

UsersRolesLoginModule 在 classpath 里查找书性文件。在以前的 JBoss EAP 版本里,放在 EAP_HOME/server/SERVER_NAME/conf/ 下的属性文件位于 classpath 上且可以轻易地被 UsersRolesLoginModule 找到。在 JBoss EAP 6 里,目录结构已经发生了变化。属性文件必须打包在应用程序里,使其在 classpath 上可用。

重要

要使修改在服务器重启后仍然生效,你必须在编辑服务器配置文件前停止服务器。
要配置基本验证的安全性,请在 standalone/configuration/standalone.xmldomain/configuration/domain.xml 服务器配置文件里的 security-domains 下添加一个新的安全域:
<security-domain name="example">
    <authentication>
        <login-module code="UsersRoles" flag="required">
            <module-option name="usersProperties" 
                    value="${jboss.server.config.dir}/example-users.properties"/>
            <module-option name="rolesProperties" 
                    value="${jboss.server.config.dir}/example-roles.properties"/>
        </login-module>
    </authentication>
</security-domain>
如果 JBoss EAP 6 实例作为独立服务器运行, ${jboss.server.config.dir} 指向 EAP_HOME/standalone/configuration/ 目录。如果它运行在受管域里, ${jboss.server.config.dir} 指向 EAP_HOME/domain/configuration/ 目录。
修改安全域的名称

在 JBoss EAP 6 里,安全域名称里不再使用前缀 java:/jaas/

  • 对于 Web 应用程序,你必须从 jboss-web.xml 里的安全域配置里删除这个前缀。
  • 对于企业级应用程序,你必须从 jboss-ejb3.xml 里的安全域配置里删除这个前缀。这个文件已经替换了 JBoss EAP 6 里的 jboss.xml

第 16 章 验证和授权

16.1. 关于验证

验证指的是确定一个主题并检验标识的真实性。最常见的验证机制是用户名和密码的组合。其他常见的机制使用共享密钥、智能卡或指纹。按照 Java EE 的声明安全性,成功验证的结果被称为 principal。
JBoss EAP 使用一个可插拔的验证模块系统,它提供灵活性以及与机构里已使用的验证系统的集成性。每个安全域都包含一个或多个配置好的验证模块。每个模块都包含其他的配置参数来自定义其行为。配置验证子系统的最简单的方法是通过基于 web 的管理控制台。
验证和授权不同,虽然他们经常联系在一起。许多包含的验证模块也可以处理授权。

16.2. 关于授权

授权是一个基于身份来赋予或拒绝对资源访问的机制。它作为一系列可以赋予 principal 的声明式安全角色来实现。
JBoss EAP 使用一个模块化系统来配置授权。每个安全域都可以获得一个或多个授权策略。每个策略都有一个定义自身行为的基本模块。它也可以通过特定的标记和属性来配置。配置授权子系统的最简单的方法是使用基于 web 的管理控制台。
授权不同于验证,它通常在验证之后发生。很多验证模块也可以处理授权。

16.3. Java 认证和授权服务(JAAS)

Java 验证和授权服务(Java Authentication and Authorization Service,JAAS)是由一系列为用户验证和授权设计的 Java 软件包组成的安全性 API。这个 API 是对标准可插拔验证模块(Pluggable Authentication Modules,PAM)框架的一个实现。它扩展了 Java EE 的访问控制架构以支持基于用户的授权。
在 JBoss EAP 里,JAAS 只提供声明式的基于角色的安全性。关于声明式安全性的更多信息,请参考 第 2.4 节 “关于声明式安全性”
JAAS 独立于任何底层的验证技术,如 Kerberos 或 LDAP。你可以不修改应用程序代码而修改底层的安全性结构,你只需要修改 JAAS 配置。

16.4. 关于 Java 认证和授权服务(JAAS)

JBoss EAP 6 的安全性架构由安全性配置子系统、包含在几个配置文件里的应用程序专有的安全性配置以及实现为 MBean 的 JAAS 安全性管理者组成。
与、服务器组和服务器专有的配置

服务器组(受管域里)和服务器(独立服务器里)包含了安全域的配置。安全域包括合并验证、授权、映射和审计模块的信息以及配置细节。应用程序在 jboss-web.xml 里通过名字指定它需要哪个安全域。

应用程序专有的配置

应用程序专有的配置出现在一个或多个下列的四个文件里。

表 16.1. 应用程序专有的配置文件

文件 描述
ejb-jar.xml
EJB 应用程序的部署描述符位于 META-INF 目录。请使用 ejb-jar.xml 来指定角色并映射到应用程序级别的 principal 上。你也可以将特定的方法和类限制到某些角色。这也可以用于其他和安全性无关的 EJB 专有的配置。
web.xml
Java EE web 应用程序的部署描述符。请使用 web.xml 来声明应用程序用于验证和授权的安全域,以及资源和传输约束,如限制允许哪种类型的 HTTP 请求。你也可以在这个文件里配置简单的基于 web 的验证。它也可以用于和安全性无关的其他应用程序专有的配置。
jboss-ejb3.xml
包含对 ejb-jar.xml 描述符的 JBoss 专有的扩展内容。
jboss-web.xml
包含对 web.xml 描述符的 JBoss 专有的扩展内容。

注意

ejb-jar.xmlweb.xml 是在 Java EE 规格里定义的。jboss-ejb3.xml 提供了对 ejb-jar.xml 的 JBoss 专有的扩展,而 jboss-web.xml 提供了对 web.xml 的 JBoss 专有的扩展。
JAAS 安全性管理者 MBean

Java 验证和授权服务(Java Authentication and Authorization Service,JAAS)是一个 Java 应用程序里用户级别安全性的框架,它使用可插拔的验证模块(Pluggable Authentication Module,PAM)。它被集成到 Java Runtime Environment(JRE)里。在 JBoss EAP 里,容器端的组件是 org.jboss.security.plugins.JaasSecurityManager MBean。它提供了对 AuthenticationManagerRealmMapping 接口的默认实现。

JaasSecurityManager MBean 根据 EJB 或 web 部署描述符文件里指定的安全域集成到 EJB 和 web 容器层里。当部署一个应用程序时,容器将部署描述符里指定的安全域和容器的安全性管理者实例相关联。按照服务器组或独立服务器上所配置的,安全性管理者将执行安全域的配置。
在客户和容器间使用 JAAS 的交互流程

JaasSecurityManager 使用 JAAS 软件包来实现 AuthenticationManager 和RealmMapping 接口行为。特别是,它的行为源于 JaasSecurityManager 已经分配的安全域里配置的登录模块实例的执行。登录模块实现了安全域的 principal 验证和角色映射行为。通过插入域的不同的登录模块配置,你可以在不同的安全域里使用 JaasSecurityManager。

为了说明 JaasSecurityManager 是如何使用 JAAS 验证过程的,下面的步骤概述了实现了 EJBHome 方法的客户端方法调用。EJB 已经部署在服务器里,它的 EJBHome 接口方法已经使用 ejb-jar.xml 里的 <method-permission> 元素进行了保护。它使用 jboss-ejb3.xml 里的 <security-domain> 元素指定的 jwdomain 安全域。下面的图表展示了这些步骤,我们在后面将进行解释。
EJB 的验证步骤

图 16.1. 安全的 EJB 方法调用的步骤

  1. 客户端执行了一个 JAAS 登录来建立用于验证的 principal 和凭证。这在图表里被标注为 Client Side Login。它也可以通过 JNDI 来执行。
    要执行 JAAS 登录,你可以创建一个 LoginContext 实例并传入要使用的配置的名称。在这里,配置的名称是 other。这个一次性的登录将登录 principal 以及凭证和随后的所有 EJB 调用相关联。这个过程并不需要验证用户。客户端登录的性质取决于客户使用的登录模块配置。在这个例子里,other 客户端登录配置条目使用了 ClientLoginModule 登录模块。这个模块绑定用户名和密码到 EJB 调用层以供服务器上之后的验证。客户端标识符不在客户端进行验证。
  2. 客户端包含 EJBHome 方法并在服务器上调用它。这个调用包含客户端传入的方法参数,以及来自客户端 JAAS 登录的用户标识符和凭证。
  3. 在服务器上,安全性拦截器验证调用方法的用户。这涉及了另外一个 JAAS 登录。
  4. 安全域确定了登录模块的选择。安全域的名字作为登录配置条目名称被传入 LoginContext 构造器。EJB 安全域是 jwdomain。如果 JAAS 验证成功,JAAS 主题将被创建。JAAS 主题将包括一个 PrincipalSet,它有如下内容:
    • 一个 java.security.Principal 实例,它对应部署安全环境的客户端标识符。
    • 名为 Rolesjava.security.acl.Group,它包含用户应用程序域的角色名称。类型为 org.jboss.security.SimplePrincipal 的对象代表角色名称。这些对象按照 ejb-jar.xml 里的约束和 EJBContext.isCallerInRole(String) 方法实现检验对 EJB 方法的访问。
    • 名为 CallerPrincipal 的可选 java.security.acl.Group,它包含一个对应应用程序域的调用者的标识符的 org.jboss.security.SimplePrincipal。CallerPrincipal 组成员是 EJBContext.getCallerPrincipal() 方法返回的值。这个映射允许可操作安全环境里的 Principal 映射到应用程序已知的 Principal。在缺乏 CallerPrincipal 映射的情况下,可操作的 principal 和应用程序域 principal 是相同的。
  5. 服务器检验调用 EJB 方法的用户是否具有权限。执行这个授权涉及两个步骤:
    • 获取被允许从 EJB 容器访问 EJB 方法的角色的名称。这些角色名称由 ejb-jar.xml 的 <role-name> 元素和包含被调用方法的 <method-permission> 元素来确定。
    • 如果没有分配角色,或者方法是在一个 exclude-list 元素里指定的,那对这个方法的访问将被拒绝。否则,安全性拦截器将在安全管理者上调用 doesUserHaveRole 方法以检查调用者是否具有分配的角色名称。这个方法迭代角色名称并检查已验证的用户的 Subject Roles 组是否包含具有分配的角色名称的 SimplePrincipal。如果任何角色名称是 Roles 组的成员,访问将被允许。而如果任何一个角色名称都不是成员,访问将被拒绝。
    • 如果 EJB 使用一个自定义的安全代理,那这个方法调用将被委托给这个代理。如果安全大力拒绝了对调用者的访问,它将抛出 java.lang.SecurityException。否则,对 EJB 方法的访问将被允许,且方法调用将传递给下一个容器拦截器。SecurityProxyInterceptor 处理这个检查且这个拦截器没有被显示。
    • 对于 web 连接请求,web 服务器将检查匹配请求的资源和被访问的 HTTP 方法的、 web.xml 里定义的安全性约束。
      如果存在对于这个请求的约束,web 服务器将调用 JaasSecurityManager 来执行 principal 验证,从而确保用户角色和 principal 对象相关联。

16.5. Java 容器授权合约(JACC)

16.5.1. 关于 Java 容器授权合约(JACC)

Java 容器授权合约(Java Authorization Contract for Containers,JACC)是一个定义容器和授权服务提供者之间合约的标准,它规范容器使用的提供者实现。它是由 JSR-115 定义的,你可以在 Java Community Process 网站上找到相关信息:http://jcp.org/en/jsr/detail?id=115。从 Java EE 1.3 以后,它已经是核心 Java EE 规格的一部分了。
JBoss EAP 通过安全子系统的安全性功能实现了对 JACC 的支持。

16.5.2. 配置 Java 容器授权合约(JACC)的安全性

要配置 JACC,你需要用正确的模块配置你的安全域,然后修改 jboss-web.xml 来包含正确的参数。
为安全域添加 JACC 支持

要为安全域添加 JACC 支持,请添加 JACC 授权策略到安全域的授权栈里,并设置 required 标记。下面是一个带有 JACC 支持的安全域的例子。然而,安全域是在管理控制台或 CLI 里,而不是直接在 XML 里配置的。

<security-domain name="jacc" cache-type="default">
    <authentication>
        <login-module code="UsersRoles" flag="required">
        </login-module>
    </authentication>
    <authorization>
        <policy-module code="JACC" flag="required"/>
    </authorization>
</security-domain>

配置 Web 应用程序以使用 JACC

jboss-web.xml 位于你的部署的 META-INF/WEB-INF/ 目录里,且包含用 web 容器的覆盖选项和其他的 JBoss 专有的配置。要使用启用了 JACC 的安全域,你需要包括 <security-domain> 元素并设置 <use-jboss-authorization> 元素为 true。下面的应用程序是用上面的 JACC 安全域进行正确配置的。

<jboss-web>
    <security-domain>jacc</security-domain>
    <use-jboss-authorization>true</use-jboss-authorization>
</jboss-web>

配置 EJB 应用程序来使用 JACC

配置 EJB 使用安全域并使用 JACC 对于不同的 Web 应用程序是不同的。对于 EJB,你可以在 ejb-jar.xml 里为一个方法或方法组声明 method permissions。在 <ejb-jar> 元素里,任何子 <method-permission> 元素都包含关于 JACC 角色的信息。详情请参考示例配置。EJBMethodPermission 类是 Java EE 6 API 的一部分,且 http://docs.oracle.com/javaee/6/api/javax/security/jacc/EJBMethodPermission.html 里有相关的文档。

例 16.1. EJB 里的 JACC 方法权限示例

<ejb-jar>
  <method-permission>
    <description>The employee and temp-employee roles may access any method of the EmployeeService bean </description>
    <role-name>employee</role-name>
    <role-name>temp-employee</role-name>
    <method>
      <ejb-name>EmployeeService</ejb-name>
      <method-name>*</method-name>
    </method>
  </method-permission>
</ejb-jar>
	      

你也可以通过安全域约束 EJB 的验证和授权机制,就像对 Web 应用程序所做的一样。安全域在 jboss-ejb3.xml 描述符里的 <security> 子元素里声明。除了安全域以外,你也可以指定 run-as principal,它可以修改运行 EJB 的 principal。

例 16.2. EJB 里的安全域声明示例


<security>
  <ejb-name>*</ejb-name>
  <security-domain>myDomain</s:security-domain>
  <run-as-principal>myPrincipal</s:run-as-principal>
</s:security>


16.6.  Java 容器验证 SPI(JASPI)

16.6.1. 关于 Java 容器验证 SPI(JASPI)的安全性

容器安全性的 Java 验证 SPI(JASPI 或 JASPIC)是一个 Java 程序的可插拔接口。它是在 Java Community Process 的 JSR-196 里定义的。关于它的规格详情,请参考 http://www.jcp.org/en/jsr/detail?id=196

16.6.2. 配置 Java 容器验证 SPI(JASPI)的安全性

要验证 JASPI 提供者,请添加 <authentication-jaspi> 元素到你的安全域里。其配置和标准的验证模块类似,但登录模块元素包含在 <login-module-stack> 元素里。它的配置的结构是:

例 16.3. authentication-jaspi 元素的结构

<authentication-jaspi>
	<login-module-stack name="...">
	  <login-module code="..." flag="...">
	    <module-option name="..." value="..."/>
	  </login-module>
	</login-module-stack>
	<auth-module code="..." login-module-stack-ref="...">
	  <module-option name="..." value="..."/>
	</auth-module>
</authentication-jaspi>


登录模块自身也以标准验证模块完全相同的方式进行配置。
因为基于 web 的管理控制台没有开放 JASPI 验证模块的配置,在直接添加配置到 EAP_HOME/domain/configuration/domain.xmlEAP_HOME/standalone/configuration/standalone.xml 之前你需要完全停止 JBoss EAP。

附录 A. 参考

A.1. 包括的验证模块

JBoss EAP 包含了下面的验证模块。其中一些模块处理授权以及验证。它们通常含有 Code 名称里的 Role
当你配置这些模块时,请使用 Code 值或完整名称(软件包限定)来引用模块。

表 A.1. Client

代码
Client
分类
org.jboss.security.ClientLoginModule
描述
这个登录模块的目的是当 JBoss EAP 充当客户时建立调用者标识符和凭证。它不应该作为用于实际的服务器验证的安全域的一部分。

表 A.2. 客户模块选项

选项 类型 默认 描述
multi-threaded
truefalse
false
如果每个线程都有自己的 principal 和凭证存储,请将其设置为 true。false 则指定虚拟机里的所有线程都共享系统的标识符和凭证。
password-stacking
useFirstPassfalse
false
设置为 useFirstPass 表示这个登录模块应该寻找存储在 LoginContext 里的信息以用作标识符。当堆积这个登录模块和其他模块时可以使用这个选项。
>restore-login-identity
truefalse
false
如果在 login() 方法的开始遇到的标识符和凭证在 logout() 被调用后要重新存储,请将其设置为 true。

表 A.3. Certificate

代码
Certificate
分类
org.jboss.security.auth.spi.BaseCertLoginModule
描述
这个登录模块的目的是基于 X509 Certificates 验证用户。其中一个用例是 web 应用程序的 CLIENT-CERT 验证。

表 A.4. Certificate Module Options

选项 类型 默认 描述
securityDomain
具有持有信任证书的信任库的 JSSE 配置的安全域的名称。
verifier
分类
用户登录证书检验的 org.jboss.security.auth.certs.X509CertificateVerifier 的类名。

表 A.5. CertificateUsers

代码
CertificateUsers
分类
org.jboss.security.auth.spi.UsersRolesLoginModule
描述
使用属性资源。第一个映射用户名和密码,第二个映射用户名和角色。

表 A.6. CertificateUsers 模块选项

选项 类型 默认 描述
unauthenticatedIdentity
字符串
定义应该分配给不包含验证信息的请求的 principal 名称。这允许不受保护的 servlet 调用不要求专有角色的 EJB 上的方法。这样的 principal 没有关联的角色且只访问未设置安全性的 EJB 或者和 unchecked permission 约束关联的 EJB。
password-stacking
useFirstPassfalse
false
设置为 useFirstPass 表示这个登录模块应该寻找存储在 LoginContext 里的信息以用作标识符。当堆积这个登录模块和其他模块时可以使用这个选项。
hashAlgorithm 字符串
用于 hash 密码的 java.security.MessageDigest 算法的名称。这个选项没有默认值,你必须显性地设置它来启用哈希算法。当指定了哈希算法时,CallbackHandler 里包含的明文密码将在作为 inputPassword 参数传递给 UsernamePasswordLoginModule.validatePassword 前进行 hash。保存在 users.properties 文件里的密码必须进行同等的 hash。关于 java.security.MessageDigest 和这个类支持的算法的更多信息,请参考 http://docs.oracle.com/javase/6/docs/api/java/security/MessageDigest.html
hashEncoding
base64hex
base64
如果设置了 hashAlgorithm,哈希密码的字符串格式。
hashCharSet
字符串
容器的环境里的默认编码集。
将明文密码转换为字节队列的编码。
usersProperties
属性文件或资源的全限定文件路径
users.properties
包含用户和密码间映射的文件。这个文件里的每个属性的格式都是 username=password
rolesProperties
属性文件或资源的全限定文件路径
roles.properties
包含用户和密码间角色的文件。这个文件里的每个属性的格式都是 username=role1,role2,...,roleN
ignorePasswordCase
truefalse
false
密码的比较是否应该忽略大小写。当哈希密码不明显时这对于编码是很重要的。
principalClass
全限定类名。
包含一个将 String 参数用作 principal 名称的构造器的 Principal 实现类。
roleGroupSeparator
单个字符
. (单一句号)
用来将用户名从 rolesGroup 文件里的角色组名里分离的字符。
defaultUsersProperties
defaultUsers.properties
如果未找到 usersProperties 文件所使用的资源或文件的名称。
defaultRolesProperties
defaultRoles.properties
如果未找到 rolesProperties 文件所使用的资源或文件的名称。
hashUserPassword
truefalse
true
当指定了 hashAlgorithm 时是否 hash 用户输入的密码。默认为 true
hashStorePassword
truefalse
true
当指定了 hashAlgorithm 时是否 hash 从 getUsersPassword() 返回的存储密码。
digestCallback
全限定类名。
包含 pre 或 post 摘要内容(如 salt 值)的 org.jboss.crypto.digest.DigestCallback 实现的类名。它只有在指定了 hashAlgorithm 时才被使用。
storeDigestCallback
全限定类名。
包含 pre 或 post 摘要内容(如哈希存储密码的 salt 值)的 org.jboss.crypto.digest.DigestCallback 实现的类名。它只有在 hashStorePassword 为 true 且指定了 hashAlgorithm 时才被使用。
callback.option.STRING
不同的
所有以 callback.option. 为前缀的选项都会传递给 DigestCallback.init(Map) 方法。收入用户名总是通过 javax.security.auth.login.name 选项传递的,而输入/存储密码是通过 digestCallbackstoreDigestCallbackjavax.security.auth.login.password 选项来传递的。

表 A.7. CertificateRoles

代码
CertificateRoles
分类
org.jboss.security.auth.spi.CertRolesLoginModule
描述
这个登录模块扩展了 Certificate 登录模块以从属性文件添加角色映射能力。它使用和 Certificate 登录模块相同的所有选项,并添加了如下选项。

表 A.8. CertificateRoles 模块选项

选项 类型 默认 描述
rolesProperties
字符串
roles.properties
包含分配给每个用户的资源或文件的名称。角色属性文件里的格式必须是 username=role1,role2,其中 username 是证书的 DN,不包括任何 =(等号)和空格字符。下面的例子是正确的格式:
CN\=unit-tests-client,\ OU\=Red\ Hat\ Inc.,\ O\=Red\ Hat\ Inc.,\ ST\=North\ Carolina,\ C\=US=JBossAdmin
defaultRolesProperties
字符串
defaultRoles.properties
如果未找到 rolesProperties 文件所使用的资源或文件的名称。
roleGroupSeparator
单个字符
. (单一句号)
作为 roleProperties 文件里的角色组分隔符的字符。

表 A.9. Database

代码 Database
分类
org.jboss.security.auth.spi.DatabaseServerLoginModule
描述
支持验证和角色映射的基于 JDBC 的登录模块。这基于具有下列定义的两个逻辑表。
  • Principals: PrincipalID (text), Password (text)
  • Roles: PrincipalID (text), Role (text), RoleGroup (text)

表 A.10. Database 模块选项

选项 类型 默认 描述
dsJndiName
JNDI 资源
保存验证信息的 JNDI 资源的名称。这个选项是必需的。
principalsQuery
prepared SQL 语句
select Password from Principals where PrincipalID=?
获取 principal 的信息的 prepared SQL 查询。
rolesQuery
prepared SQL 语句
select Role, RoleGroup from Roles where PrincipalID=?
获取角色信息的 prepared SQL 查询。它应该和 select Role, RoleGroup from Roles where PrincipalID=? 相等,这里的 Role 是角色名称而 RoleGroup 的值总是带有大写 RRolesCallerPrincipal

表 A.11. DatabaseCertificate

代码
DatabaseCertificate
分类
org.jboss.security.auth.spi.DatabaseCertLoginModule
描述
这个登录模块扩展了 Certificate 登录模块以从数据库表添加角色映射能力。它使用和 Certificate 登录模块相同的所有选项,并添加了如下选项。

表 A.12. DatabaseCertificate 模块选项

选项 类型 默认 描述
dsJndiName
JNDI 资源
保存验证信息的 JNDI 资源的名称。这个选项是必需的。
rolesQuery
prepared SQL 语句
select Role,RoleGroup from Roles where PrincipalID=?
为了映射角色的 prepared SQL 语句。它应该和 select Role, RoleGroup from Roles where PrincipalID=? 相等,这里的 Role 是角色名称而 RoleGroup 的值总是带有大写 RRolesCallerPrincipal
suspendResume
truefalse
true
在数据库操作期间现有的 JTA 事务是否应该被暂停。

表 A.13. Identity

代码
Identity
分类
org.jboss.security.auth.spi.IdentityLoginModule
描述
关联这个模块选项里指定的 principal 和任何针对这个模块验证的主题。所使用的 Principal 类的类型是 org.jboss.security.SimplePrincipal。如果没有指定 principal 选项,那使用的名称是 guest

表 A.14. Identity 模块选项

选项 类型 默认 描述
principal
字符串
guest
用于 principal 的名称。
roles
用逗号隔开的字符串的列表
将分配给主题的用逗号隔开的角色列表。

表 A.15. Ldap

代码
Ldap
分类
org.jboss.security.auth.spi.LdapLoginModule
描述
当用户名和密码存储在可通过 JNDI LDAP 供应商访问的 LDAP 服务器上时进行的验证。许多选项不是必需的,因为它们可由 LDAP 供应商或系统环境来决定。

表 A.16. Ldap 模块选项

选项 类型 默认 描述
java.naming.factory.initial
类名
com.sun.jndi.ldap.LdapCtxFactory
InitialContextFactory 实现的类名。
java.naming.provider.url
ldap:// URL
LDAP 服务器的 URL。
java.naming.security.authentication
nonesimple 或 SASL 机制的名称。
simple
用于绑定 LDAP 服务器的安全级别。
java.naming.security.protocol
传输协议
如果未指定,则由供应商决定。
用于安全访问的传输协议,如 SSL。
java.naming.security.principal
字符串
用于验证调用者的 principal 的名称。它是根据下面描述的属性构建的。
java.naming.security.credentials
凭证类型
验证模式使用的凭证类型。其中一些例子包括哈希密码、明文密码、密钥或证书。如果没有指定这个属性,其行为将由服务供应商决定。
principalDNPrefix
字符串
添加到用户名以组成用户 DN 的前缀。你可以提示用户输入用户名并使用 principalDNPrefixprincipalDNSuffix 构建全限定 DN。
principalDNSuffix
添加到用户名以组成用户 DN 的后缀。你可以提示用户输入用户名并使用 principalDNPrefixprincipalDNSuffix 构建全限定 DN。
useObjectCredential
truefalse
false
凭证是否应该用 org.jboss.security.auth.callback.ObjectCallback 类型的回调方法作为不透明的对象、还是用 JAAS PasswordCallback 作为字符数组密码获得。这允许传递非字符数组的凭证信息到 LDAP 服务器。
rolesCtxDN
全限定的 DN
用于搜索用户角色的上下文的全限定 DN。
userRolesCtxDNAttributeName
属性
包含搜索用户角色的上下文 DN 的用户对象里的属性。它和 rolesCtxDN 的区别是搜索用户角色的上下文可能对于每个用户都是唯一的。
rolesAttributeID
属性
roles
包含用户角色的属性的名称。
rolesAttributeIsDN
truefalse
false
roleAttributeID 是否包含角色对象的全限定 DN。如果为 false,角色名将从上下文名称的 roleNameAttributeId 属性值里获取。某些目录模式,如 Microsoft Active Directory,要求这个属性的值为 true
rolesNameAttributeID
属性
group
包含角色名称的 roleCtxDN 上下文里的属性的名称。如果 roleAttributeIsDN 属性为 true,这个属性将被用来查找角色对象的 name 属性。
uidAttributeID
属性
uid
UserRolesAttributeDN 里对应用户 ID 的属性的名称。它被用来定位用户角色。
matchOnUserDN
truefalse
false
对用户角色搜索是否应该匹配用户的全限定 DN 或只是用户名而已。如果为true,完整的用户 DN 将作为匹配值。如果为 false,则只使用用户名来匹配 uidAttributeName 属性。
allowEmptyPasswords
truefalse
true
是否允许空的密码。多数 LDAP 服务器将空密码视同匿名登录尝试。要拒绝空密码,请将它设置为 false。

表 A.17. LdapExtended

代码
LdapExtended
分类
org.jboss.security.auth.spi.LdapExtLoginModule
描述
另外的一个使用搜索来定位绑定用户和关联角色的 LDAP 登录模块实现。角色队列递归地解析 DN 来导航分层的角色结构。它使用和 LDAP 模块相同的 java.naming 选项以及下列 LDAP 模块没有的选项。
这个验证以两步进行:
  1. 对 LDAP 服务器的初始绑定是使用 bindDN 和 bindCredential 选项来完成的。bindDN 是一个具有搜索 baseCtxDN 和 rolesCtxDN 树里的用户和角色能力的 LDAP 用户。要验证的用户 DN 是通过 baseFilter 属性指定的过滤器来进行查询的。
  2. 结果用户 DN 是通过将用户 DN 作为 InitialLdapContext 环境的 Context.SECURITY_PRINCIPAL 绑定到 LDAP 服务器来验证的。Context.SECURITY_CREDENTIALS 属性被设置为回调处理程序获得的 String 型的密码。

表 A.18. LdapExtended 模块选项

选项 类型 默认 描述
baseCtxDN
全限定的 DN
开始用户搜索的顶层上下文的固定 DN。
bindDN
全限定的 DN
用户和角色查询里用来绑定 LDAP 服务器的 DN。这个 DN 需要读取和搜索 baseCtxDNrolesCtxDN 值上的权限。
bindCredential
字符串,可以进行加密。
用于 bindDN 的密码。如果指定了 jaasSecurityDomain,它可以被加密。
jaasSecurityDomain
JMX ObjectName
用来解密 bindCredentialJaasSecurityDomain 的 JMX ObjectName。密码的解密使用 JaasSecurityDomain.encrypt64(byte[]) 方法返回的格式。
baseFilter
LDAP 过滤器字符串
用来定位要验证的用户的上下文的搜索过滤器。从登录模块回调方法里获得的输入用户名或用户 DN 将替换至过滤器里的 {0} 表达式。搜索过滤器的一个常见例子是 (uid={0})
rolesCtxDN
全限定的 DN
用于搜索用户角色的上下文的固定 DN。这不是实际角色的 DN,它是包含用户角色的对象所在的 DN。例如,在 Microsoft Active Directory 服务器里,它是用户帐号所在的 DN。
roleFilter
LDAP 过滤器字符串
用来定位和验证用户相关联的角色的搜索过滤器。从登录模块回调方法里获得的输入用户名和用户 DN 将被替换过滤器里的 {0} 表达式。已验证的用户 DN 将替换过滤器里的 {1} 表达式。匹配输入用户名的搜索过滤器示例是 (member={0})。对应已验证的用户 DN 的例子是 (member={1})
roleAttributeIsDN
truefalse
false
roleAttributeID 是否包含角色对象的全限定 DN。如果为 false,角色名将从上下文名称的 roleNameAttributeId 属性值里获取。某些目录模式,如 Microsoft Active Directory,要求这个属性的值为 true
defaultRole
角色名称
用于所有已验证用户的角色
parseRoleNameFromDN
truefalse
false
指定查询返回的 DN 是否包含 roleNameAttributeID。如果设置为 true,将检查 DN 里是否有 roleNameATtributeID,如果为 false,将不会检查。这个标记可以提高 LDAP 查询的性能。
parseUsername
truefalse
false
指定 DN 是否对用户名进行解析的标记。如果为 true,DN 将对用户名进行解析。如果为 false,DN 将不对用户名进行解析。这个选项是和 usernameBeginString 及 usernameEndString 一起使用的。
usernameBeginString
字符串
定义将从 DN 的开头删除以显示用户名的字符串。这个选项是和 usernameEndString 一起使用的。
usernameEndString
字符串
定义将从 DN 的结尾删除以显示用户名的字符串。这个选项是和 usernameBeginString 一起使用的。
roleNameAttributeID
属性
group
包含角色名称的 roleCtxDN 上下文里的属性的名称。如果 roleAttributeIsDN 属性为 true,这个属性将被用来查找角色对象的 name 属性。
distinguishedNameAttribute
属性
distinguishedName
包含用户 DN 的用户条目里的属性的名称。如果用户自身的 DN 包含特殊字符(如反斜杠)而阻止了正确的用户映射,这就是有必要的。如果这个属性不存在,条目的 DN 将会被使用。
roleRecursion
整数
0
角色搜索的递归级别数。禁用递归可将其设置为 0
searchTimeLimit
整数
10000 (10 秒)
用户或角色搜索的超时时间(毫秒)。
searchScope
OBJECT_SCOPE, ONELEVEL_SCOPE, SUBTREE_SCOPE 中的一个
SUBTREE_SCOPE
使用的搜索作用域
allowEmptyPasswords
truefalse
true
是否允许空的密码。多数 LDAP 服务器将空密码视同匿名登录尝试。要拒绝空密码,请将它设置为 false。

表 A.19. RoleMapping

代码
RoleMapping
分类
org.jboss.security.auth.spi.RoleMappingLoginModule
描述
映射作为验证过程的最终结果的角色到声明式角色。当你添加这个模块到安全域里时,它必须标记为 optional

表 A.20. RoleMapping 模块选项

选项 类型 默认 描述
rolesProperties
属性文件或资源的全限定文件路径
roles.properties
映射角色到替代角色的属性文件或资源的全限定文件路径。其格式是 original_role=role1,role2,role3
replaceRole
truefalse
false
是否添加当前的角色,或者用映射的角色替换当前的角色。设为 true 则进行替换。

表 A.21. RunAs

代码
RunAs
分类
Class: org.jboss.security.auth.spi.RunAsLoginModule
描述
这是一个 Helper 模块,它在验证的登录阶段将 run as 角色推入栈,并在提交或中止阶段从栈里弹出 run as 角色。这个登录模块为其他必须访问安全资源以执行验证的登录模块(如访问安全 EJB 的登录模块)提供了一个角色。在要求 run as 角色的登录模块建立之前,你必须先配置好 RunAsLoginModule

表 A.22. RunAs 选项

选项 类型 默认 描述
roleName
角色名称
nobody
在登录阶段用作 run as 角色的角色的名称。

表 A.23. Simple

代码
Simple
分类
org.jboss.security.auth.spi.SimpleServerLoginModule
描述
用于测试目的的快速设置安全性的模块。它实现了下列简单的算法:
  • 如果密码为 null,验证用户并分配一个 guest 标识符和 guest 角色。
  • 否则,如果密码和用户相同,分配一个和用户名相同的标识符以及 adminguest 角色。
  • 否则,验证将会失败。
Simple 模块选项

Simple 模块没有选项。

表 A.24. ConfiguredIdentity

代码
ConfiguredIdentity
分类
org.picketbox.datasource.security.ConfiguredIdentityLoginModule
描述
关联这个模块选项里指定的 principal 和任何针对这个模块验证的主题。所使用的 Principal 类的类型是 org.jboss.security.SimplePrincipal

表 A.25. ConfiguredIdentity 模块选项

选项 类型 默认 描述
principal
principal 的名称。
guest
将和针对这个模块验证的任何主题关联的 principal。

表 A.26. SecureIdentity

代码
SecureIdentity
分类
org.picketbox.datasource.security.SecureIdentityLoginModule
描述
提供这个模块只是为了和之前的系统兼容。它允许你加密密码并和静态 principal 一起使用这个密码。如果你的应用程序使用了 SecureIdentity,请考虑使用密码库机制。

表 A.27. SecureIdentity 模块选项

选项 类型 默认 描述
username
用于验证的用户名。
password
加密的字符串
用于验证的密码。要加密这个密码,请在命令行直接使用这个模块。
java org.picketbox.datasource.security.SecureIdentityLoginModule password_to_encrypt
将这个命令的运行结果粘贴到模块选项的 value 字段。
managedConnectionFactoryName
JCA 资源
数据源的 JCA 连接工厂的名称。

表 A.28. PropertiesUsers

代码
PropertiesUsers
分类
org.jboss.security.auth.spi.PropertiesUsersLoginModule
描述
使用一个属性文件来存储用户名和密码。它没有提供授权(角色映射)。这个模块只适合于测试用途。

表 A.29. PropertiesUsers 模块选项

选项 类型 默认 描述
properties
Java 属性文件或资源的全限定文件路径和名称。
用于验证的包含用户名和明文密码的属性文件。

表 A.30. SimpleUsers

代码
SimpleUsers
分类
org.jboss.security.auth.spi.SimpleUsersLoginModule
描述
这个登录模块在一个 Java 属性文件里保存了用户名和明文密码。这只是用于测试目的,不适合用于产品环境里。

表 A.31. SimpleUsers 模块选项

选项 类型 默认 描述
username
用于验证的用户名。
password
用于验证的明文密码。

表 A.32. LdapUsers

代码
LdapUsers
分类
org.jboss.security.auth.spi.LdapUsersLoginModule
描述
LdapUsers 模块被 ExtendedLDAPAdvancedLdap 模块取代。

表 A.33. Kerberos

代码
Kerberos
分类
com.sun.security.auth.module.Krb5LoginModule
描述
用 GSSAPI 执行Kerberos 登录验证。这个模块不是 Sun Microsystems 提供的 API 里的安全框架的一部分。细节可以在 http://docs.oracle.com/javase/1.4.2/docs/guide/security/jaas/spec/com/sun/security/auth/module/Krb5LoginModule.html 里找到。这个模块需要和另外一个处理验证和角色映射的模块配对。

表 A.34. Kerberos 模块选项

选项 类型 默认 描述
storekey
truefalse
false
是否添加 KerberosKey 到主题的私有凭证。
doNotPrompt
truefalse
false
如果设置为 true,用户将不会被提示输入密码。
useTicketCache
布尔值,truefalse
.
false
如果为 true,GTG 将从票据缓存里获取。如果为 false,将不会使用票据缓存。
ticketcache
代表 Kerberos 票据缓存的文件或资源。
默认值取决于你所使用的操作系统。
  • 红帽企业版 Linux / Solaris: /tmp/krb5cc_uid,使用操作系统的数字 UID 值。
  • Microsoft Windows Server: 使用 Local Security Authority (LSA) API 来查找票据缓存。
票据缓存的位置。
useKeyTab
truefalse
false 是否从密钥表文件里获取 principal 的密钥。
keytab
代表 Kerberos keytab 的文件或资源。
操作系统的 Kerberos 配置文件的位置,或者 /home/user/krb5.keytab
密钥表文件的位置。
principal
字符串
Principal 的名称。这可以是简单的用户名或服务名,如 host/testserver.acme.com。或者当密钥表包含多个 principal 时,使用它而不是从密钥表里获取 principal。
useFirstPass
truefalse
false
是否以从 javax.security.auth.login.namejavax.security.auth.login.password 为关键字从模块的共享状态获取用户名和密码。如果验证失败,不会进行重试。
tryFirstPass
truefalse
false
useFirstPass 相同,但如果验证失败,模块将使用 CallbackHandler 来获取新的用户名和密码。如果第二次验证失败,将报告给调用的应用程序。
storePass
truefalse
false
是否在模块的共享状态里保存用户名和密码。如果关键字已存在于共享内存里,或者验证失败的话,这都不会发生。
clearPass
truefalse
false
设置它为 true 在两个验证阶段都完成后从共享内存里清除用户名和密码。

表 A.35. SPNEGOUsers

代码
SPNEGOUsers
分类
org.jboss.security.negotiation.spnego.SPNEGOLoginModule
描述
允许在 Microsoft Active Directory 服务器或其他支持 SPNEGO 的环境里进行 SPNEGO 验证。SPNEGO 也可以包含 Kerberos 凭证。这个模块需要和另外一个处理验证和角色映射的模块配对。

表 A.36. SPNEGO 模块选项

选项 类型 默认 描述
storeKey
truefalse
false
是否保存密钥。
useKeyTab
truefalse
false
是否使用密钥表。
principal
代表 Kerberos 验证的 principal 的字符串。
用于验证的 principal 的名称。
keyTab
代表 keytab 的文件或资源。
none
密钥表的位置。
doNotPrompt
truefalse
false
是否提示输入密码。
debug
truefalse
false
是否记录更冗余的信息以用于调试。

表 A.37. AdvancedLdap

代码 AdvancedLdap
分类
org.jboss.security.negotiation.AdvancedLdapLoginModule
描述
提供额外功能的模块,如 SASL 和对 JAAS 安全域的使用。

表 A.38. AdvancedLdap 模块选项

选项 类型 默认 描述
bindAuthentication
用于绑定到目录服务器的 SASL 验证的类型。
jassSecurityDomain
string
要使用的 JAAS 安全域的名称。
java.naming.provider.url
string
目录服务器的 URI.
baseCtxDN
全限定标识名(DN)。
要用作搜索基础的标识名。
baseFilter
代表 LDAP 搜索过滤器的字符串。
用于缩减搜索结果的过滤器。
roleAttributeID
代表 LDAP 属性的字符串。
包含授权角色的名称的 LDAP 属性。
roleAttributeIsDN
truefalse
false
这个角色属性是否是标识名(Distinguished Name,DN)。
roleNameAttributeID
代表 LDAP 属性的字符串。
包含实际角色属性的 RoleAttributeId 里所包含的属性。
recurseRoles
truefalse
false
是否递归地搜索 RoleAttributeId 里的角色。

表 A.39. AdvancedADLdap

代码 AdvancedADLdap
分类
org.jboss.security.negotiation.AdvancedADLoginModule
描述
这个模块扩展了 AdvancedLdap 登录模块,并添加额外的和 Microsoft Active Directory 相关的参数。

表 A.40. UsersRoles

代码 UsersRoles
分类
org.jboss.security.auth.spi.UsersRolesLoginModul
描述
支持存储在两个不同属性文件里的多个用户和角色的简单登录模块。

表 A.41. UsersRoles 模块选项

选项 类型 默认 描述
usersProperties
文件或资源的路径。
users.properties
包含用户-密码映射的文件或资源。这个文件的格式是 user=hashed-password
rolesProperties
文件或资源的路径。
roles.properties
包含用户-角色映射的文件或资源。这个文件的格式是 username=role1,role2,role3
password-stacking
useFirstPassfalse
false
useFirstPass 的值表示这个登录模块应该首先查看存储在 LoginContext 里关于这个标识符的信息。当堆积这个登录模块和其他模块时可以使用这个选项。
hashAlgorithm
代表密码的哈希算法的字符串。
none
用于 hash 密码的 java.security.MessageDigest 算法的名称。这个选项没有默认值,你必须显性地设置它来启用哈希算法。当指定了 hashAlgorithm 时,CallbackHandler 里包含的明文密码将在作为 inputPassword 参数传递给 UsernamePasswordLoginModule.validatePassword 前进行 hash。保存在 users.properties 文件里的密码必须进行同等的 hash。
hashEncoding
base64hex
base64
如果设置了 hashAlgorithm,哈希密码的字符串格式。
hashCharset
字符串
容器的运行时环境里的默认编码集。
将明文密码转换为字节队列的编码。
unauthenticatedIdentity
principal 名称
定义分配给不包含验证信息的请求的 principal 名称。这允许不受保护的 servlet 调用不要求专有角色的 EJB 上的方法。这样的 principal 没有关联的角色且只访问未设置安全性的 EJB 或者和 unchecked permission 约束关联的 EJB。
自定义验证模块

验证模块是 org.jboss.security.LoginModule 的实现。关于创建自定义验证模块的更多信息,请参考相关的 API 文档。

A.2. 包括的授权模块

下面的模块提供授权服务。
代码
DenyAll org.jboss.security.authorization.modules.AllDenyAuthorizationModule
PermitAll org.jboss.security.authorization.modules.AllPermitAuthorizationModule
Delegating org.jboss.security.authorization.modules.DelegatingAuthorizationModule
Web org.jboss.security.authorization.modules.WebAuthorizationModule
JACC org.jboss.security.authorization.modules.JACCAuthorizationModule

A.3. 包括的安全映射模块

JBoss EAP 提供了下面的安全映射角色。
代码
PropertiesRoles org.jboss.security.mapping.providers.role.PropertiesRolesMappingProvider
SimpleRoles org.jboss.security.mapping.providers.role.SimpleRolesMappingProvider
DeploymentRoles org.jboss.security.mapping.providers.DeploymentRolesMappingProvider
DatabaseRoles org.jboss.security.mapping.providers.role.DatabaseRolesMappingProvider
LdapRoles org.jboss.security.mapping.providers.role.LdapRolesMappingProvider

A.4. 包括的安全审计供应商模块

JBoss EAP 提供了一个安全审计供应商。
代码
LogAuditProvider org.jboss.security.audit.providers.LogAuditProvider

A.5. jboss-web.xml 配置参考

简介

jboss-web.xml 是你的部署的 WEB-INFMETA-INF 里的一个文件。它包含 JBoss Web 容器对 Servlet 3.0 规格所添加的功能的配置信息。Servlet 3.0 规格所专有的设置位于相同目录下的 web.xml 里。

jboss-web.xml 文件里的顶层元素是 <jboss-web> 元素。
映射全局资源到 WAR 的要求

许多可用的设置将应用程序的 web.ml 里设置的要求映射到本地资源。关于 web.xml 设置的解释,请访问 http://docs.oracle.com/cd/E13222_01/wls/docs81/webapp/web_xml.html

例如,如果 web.xml要求 jdbc/MyDataSourcejboss-web.xml 可能映射全局数据源 java:/DefaultDS 来满足这个要求。WAR 使用全局数据源 jdbc/MyDataSource 来满足要求。

表 A.42. 常用的顶级属性

属性 描述
env-entry
web.xml 要求的 env-entry 的映射。
ejb-ref
web.xml 要求的 ejb-ref 的映射。
ejb-local-ref
web.xml 要求的 ejb-local-ref 的映射。
service-ref
web.xml 要求的 service-ref 的映射。
resource-ref
web.xml 要求的 resource-ref 的映射。
resource-env-ref
web.xml 要求的 resource-env-ref 的映射。
message-destination-ref
web.xml 要求的 message-destination-ref 的映射。
persistence-context-ref
web.xml 要求的 persistence-context-ref 的映射。
persistence-unit-ref
web.xml 要求的 persistence-unit-ref 的映射。
post-construct
web.xml 要求的 post-context 的映射。
pre-destroy
web.xml 要求的 pre-destroy 的映射。
data-source
web.xml 要求的 data-source 的映射。
context-root 应用程序的根上下文。默认值是部署的名称(不带 .war 后缀)。
virtual-host 应用程序接受请求的 HTTP 虚拟主机的名称。它指向 HTTP Host 头部的内容。
annotation 描述应用程序使用的注解。更多信息请参考 <annotation>
listener 描述应用程序使用的 listener。更多信息请参考 <listener>
session-config 这个元素和 web.xml<session-config> 元素的功能一样,包括它只是出于兼容性的考虑。
valve 描述应用程序使用的阀(Valve)。更多信息请参考 <valve>
overlay 添加至应用程序的覆盖(Overlay)的名称。
security-domain 应用程序使用的安全域的名称。安全域自身是通过基于 Web 的管理控制台或管理 CLI 来配置的。
security-role 这个元素和 web.xml<security-role> 元素的功能一样,它只是作为兼容性被包括的。
use-jboss-authorization 如果出现这个元素并包含了大小写敏感的值 “true”,JBoss Web 授权栈将被使用。如果它没出现或包含非 “true” 的值,那只会使用 Java EE 规格里指定的授权机制。这个元素是 JBoss EAP 6 里新引入的。
disable-audit 如果出现这个元素,Web 安全审计将被禁用。否则,它将被启用。Web 安全审计不是 Java EE 规格的一部分。这个元素是 JBoss EAP 6 里新引入的。
disable-cross-context 如果为 false,应用程序能够调用另外一个应用程序上下文。它默认为 true
下面的元素都具有子元素。
<annotation>

描述应用程序使用的注解。下表列出了 <annotation> 的子元素。

表 A.43. 注解配置元素

属性 描述
class-name
注解的类名
servlet-security
代表了 servlet 安全性的元素,如 @ServletSecurity
run-as
代表了 run-as 信息的元素,如 @RunAs
multi-part
代表了 multi-part 信息的元素,如 @MultiPart
<listener>

描述 listener。下表列出了 <listener> 的子元素。

表 A.44. Listener 配置元素

属性 描述
class-name
Listener 的类名
listener-type
condition 元素的列表,表示添加哪种 listener 到应用程序的 Context 里。有效值为:
CONTAINER
在 Context 里添加一个 ContainerListener。
LIFECYCLE
在 Context 里添加一个 LifecycleListener。
SERVLET_INSTANCE
在 Context 里添加一个 InstanceListener。
SERVLET_CONTAINER
在 Context 里添加一个 WrapperListener。
SERVLET_LIFECYCLE
在 Context 里添加一个 WrapperLifecycle。
module
包含 listener 类的模块的名称。
param
包含两个子元素的参数:<param-name><param-value>
<valve>

描述应用程序的库。它包含和 <listener> 相同的配置元素。

A.6. EJB 安全参数引用

表 A.45. EJB 安全参数元素

元素 描述
<security-identity>
包含从属于 EJB 的安全标识符的子元素。
<use-caller-identity />
指定 EJB 使用和调用者相同的安全标识符。
<run-as>
包含一个 <role-name> 元素。
<run-as-principal>
如果指定,则表示分配给转出调用的 principal。如果不指定,分配给转出调用的 principal 是 anonymous
<role-name>
指定 EJB 应该以什么角色运行。
<description>
描述 <role-name> 里命名的角色。
.

例 A.1. 安全标识符示例

这个例子展示了 表 A.45 “EJB 安全参数元素” 里描述的每个标签。它们可以用在 <servlet> 里。
<ejb-jar>
    <enterprise-beans>
        <session>
            <ejb-name>ASessionBean</ejb-name>
            <security-identity>
                <use-caller-identity/>
            </security-identity>
        </session>
        <session>
            <ejb-name>RunAsBean</ejb-name>
            <security-identity>
                <run-as>
                    <description>A private internal role</description>
                    <role-name>InternalRole</role-name>
                </run-as>
            </security-identity>
        </session>
		  <session>
			 <ejb-name>RunAsBean</ejb-name>
			 <security-identity>
				<run-as-principal>internal</run-as-principal>
			 </security-identity>
		  </session>
    </enterprise-beans>
</ejb-jar>

附录 B. Revision History

修订历史
修订 1.0.0-2Mon Jan 6 2014Xi Huang
翻譯、校閱完成
修订 1.0.0-1Mon Jan 06 2014CS Builder Robot
Built from Content Specification: 13944, Revision: 507775

法律通告

Copyright © 2014 Red Hat, Inc..
This document is licensed by Red Hat under the Creative Commons Attribution-ShareAlike 3.0 Unported License. If you distribute this document, or a modified version of it, you must provide attribution to Red Hat, Inc. and provide a link to the original. If the document is modified, all Red Hat trademarks must be removed.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, JBoss, OpenShift, Fedora, the Infinity logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
Java® is a registered trademark of Oracle and/or its affiliates.
XFS® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries.
MySQL® is a registered trademark of MySQL AB in the United States, the European Union and other countries.
Node.js® is an official trademark of Joyent. Red Hat Software Collections is not formally related to or endorsed by the official Joyent Node.js open source or commercial project.
The OpenStack® Word Mark and OpenStack logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community.
All other trademarks are the property of their respective owners.