安全指南

JBoss 企业级应用程序平台 6.2

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

Sande Gilda

Darrin Mison

David Ryan

Misty Stanley-Jones

摘要

本书是保护红帽 JBoss 企业版应用程序平台 6 及其补丁版本的指南。

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

第 1 章 简介

1.1. 关于 JBoss 企业版应用程序平台 6(JBoss EAP 6)

红帽 JBoss EAP 6 是一个构建在开放标准上并和 Java EE 6 规格兼容的快速的、安全的、功能强大的中间件平台。它集成 JBoss AS 7 和高可用性的群集、强大的 messaging 系统、分布式缓存以及其他技术来创建稳定、可扩充的和快速的平台。
新的模块化结构允许服务只在需要时才启用,这显著地提高了启动速度。管理控制台和管理命令行接口不需要手动编辑 XML 配置文件,并添加了创建脚本和自动化任务的能力。此外,它也包含了 API 和开发框架以快速开发安全的、强大的和可扩充的 Java EE 应用程序。

1.2. 关于 JBoss 企业版应用程序平台 6 的安全性

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

第 2 章 安全概述

2.1. 关于声明式安全性

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

2.1.1. Java EE 的声明式安全性概述

J2EE 的安全模型是声明式的,你可以在标准的 XML 描述符文件里描述安全角色和权限,而不需要将安全性嵌入到商业组件里。这种安全性和商业级别的代码的隔离更倾向于是关于组件部署的功能而不是组件的商业逻辑的内在方面。例如,用于访问银行帐号的自动柜员机(ATM),安全要求、角色和权限将独立于你是如何访问银行帐号的,它会基于银行管理这个帐号的方式、ATM 的位置等来确定。
对 J2EE 应用程序的保护是基于应用程序的安全性要求的规格并通过标准的 J2EE 部署描述符来实现的。对企业级应用程序里的 EJB 和 Web 组件的安全访问是通过 ejb-jar.xmlweb.xml 部署描述符来设置的。

2.1.2. 安全引用

EJB 和 Servlet 都可以声明一个或多个 <security-role-ref> 元素。
安全角色引用模型的解释

图 2.1. 安全角色引用模型

这个元素声明了组件将 <role-name> 组件的 role-nameType 属性值作为 isCallerInRole(String) 方法的参数使用。通过使用 isCallerInRole 方法,组件可以检验调用者是否具有用 <security-role-ref><role-name> 声明的角色。<role-name> 元素值必须通过 <role-link> 链接到 <security-role> 元素。isCallerInRole 的典型用法是执行一个无法用基于角色的 <method-permissions> 元素定义的安全检查。

例 2.1. ejb-jar.xml 描述符文件片段

  
  <!-- A sample ejb-jar.xml fragment -->
    <ejb-jar>
      <enterprise-beans>
        <session>
          <ejb-name>ASessionBean</ejb-name>
          ...
          <security-role-ref>
            <role-name>TheRoleICheck<role-name>
              <role-link>TheApplicationRole</role-link>
          </security-role-ref>
        </session>
      </enterprise-beans>
    ...
    </ejb-jar>

注意

这个片段只是一个例子而已。在部署里,这个部分里的元素必须包含和 EJB 部署相关的角色名称以及链接。

例 2.2. web.xml 描述符文件片段


<web-app>
  <servlet>
    <servlet-name>AServlet</servlet-name>
    ...
    <security-role-ref>
      <role-name>TheServletRole</role-name>
      <role-link>TheApplicationRole</role-link>
    </security-role-ref>
  </servlet>
    ...
</web-app>

2.1.3. 安全标识符

EJB 可以用 <security-identity> 元素指定另外一个 EJB 调用组件上的方法时必须使用的标识符。
J2EE 安全标识符模型插图

图 2.2. J2EE 安全标识符数据模型

调用标识符可以是当前调用者的标识符,也可以是专门的角色。应用程序组装者使用 <security-identity> 元素和 <use-caller-identity> 子元素。这表示对于这个 EJB 进行的方法调用,当前调用者的标识符应该作为安全标识符传播。在没有显性的 <security-identity> 元素声明时,默认会进行调用者标识符的传播。
或者,应用程序组装者可以使用 <run-as><role-name> 子元素来指定 <role-name> 元素值提供的专有安全角色必须用作 EJB 进行方法调用的安全标识符。
请注意,这并没有修改 EJBContext.getCallerPrincipal() 方法所看到的调用者的标识符。而调用者的安全角色被设置为 <run-as><role-name> 元素值指定的单一角色。
<run-as> 元素的一个用法是阻止外部客户访问内部的 EJB。你可以通过为内部 EJB 分配 <method-permission> 元素来配置这种行为,这个元素限制了对从不分配给外部客户的角色的访问。而反过来使用内部 EJB 的 EJB 必须用和受限角色相等的 <run-as><role-name> 进行配置。下面的描述符文件片段是一个<security-identity> 元素的用法示例。

<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>
    </enterprise-beans>
    <!-- ... -->
</ejb-jar>

当你使用 <run-as> 来分配专门的角色给转出调用时,名为 anonymous 的 principal 将分配给所有的转出调用。如果你想用其他的 principal 关联这个调用,你必须将 <run-as-principal>jboss.xml 文件里的 bean 进行关联。下列代码片段将名为 internal 的 principal 和前面例子里的 RunAsBean 进行关联。

<session>
    <ejb-name>RunAsBean</ejb-name>
    <security-identity>
        <run-as-principal>internal</run-as-principal>
    </security-identity>
</session>

<run-as> 元素在 web.xml 文件的 servlet 定义里也是可用的。下面的例子展示了如何将角色 InternalRole 分配给 servlet:

  <servlet>
    <servlet-name>AServlet</servlet-name>
    <!-- ... -->
    <run-as> 
        <role-name>InternalRole</role-name>
    </run-as>
  </servlet>

这个 servlet 里的调用和 ‘anonymous’ principal 相关联。jboss-web.xml 文件里的 <run-as-principal> 元素用于和 run-as 角色一起使用的专有 principal。下列代码片段展示了如何将名为 internal 的 principal 关联至上面的 servlet。

  <servlet>
    <servlet-name>AServlet</servlet-name>
    <run-as-principal>internal</run-as-principal>
  </servlet>

2.1.4. 安全角色

security-role-refsecurity-identity 引用的安全角色名称需要映射到应用程序所声明的其中一个角色。应用程序组装者通过声明 security-role 来定义安全角色。role-name 值是一个逻辑的应用程序角色名,如 Administrator、Architect、alesManager 等等。
J2EE 规格认为将部署描述符里的安全角色用于定义应用程序的逻辑安全视图是很重要的。在 J2EE 部署描述符里定义的角色不应该和用户组、用户、Principal 以及其他存在于目标企业操作环境里的概念混淆。部署描述符角色是带有应用程序专有域名称的应用程序构件。例如,banking 程序可能使用角色名如 BankManager、Teller 或 Customer。
在 JBoss EAP 里,security-role 元素只被用来映射 security-role-ref/role-name 值到组件角色引用的逻辑角色。用户的角色是应用程序的安全管理者动态分配的。JBoss 不要求定义 security-role 元素来声明方法权限。然而,我们仍然推荐使用 security-role 元素以确保跨应用服务器的可移植性且便于部署描述符的维护。

例 2.3. 解释 security-role 元素用法的 ejb-jar.xml 描述符片段。

<!-- A sample ejb-jar.xml fragment --><ejb-jar><assembly-descriptor><security-role><description>The single application role</description><role-name>TheApplicationRole</role-name></security-role></assembly-descriptor></ejb-jar>

    
        
            
            
        
    

例 2.4. 解释 security-role 元素用法的 web.xml 描述符片段。

<!-- A sample web.xml fragment --><web-app><security-role><description>The single application role</description><role-name>TheApplicationRole</role-name></security-role></web-app>

  
    
    
  

2.1.5. EJB 方法权限

应用程序组装者可以通过 method-permission 元素声明来设置被允许调用 EJB 的 home 和 remote 接口方法的角色。
对 J2EE 的 method-permission 元素的解释

图 2.3. J2EE 的 method-permission 元素

每个 method-permission 元素都包含一个或多个定义逻辑角色的 role-name 子元素,这些逻辑角色可以访问 method-child 元素确定的 EJB 方法。你也可以指定 unchecked 而不是 role-name 元素来声明任何验证用户都可以访问 method-child 元素确定的 EJB 方法。此外,你可以声明无人可以访问具有 exclude-list 元素的方法。如果 EJB 具有没有用 method-permission 元素声明可以访问的方法,那 EJB 方法默认是不能被使用的。这等同于将方法默认放入 exclude-list 元素里。
对 J2EE 方法元素的解释

图 2.4. J2EE 方法元素

方法元素声明支持三种风格。
第一种用来引用 EJB 的所有的 home 和 component 接口方法:
<method><ejb-name>EJBNAME</ejb-name><method-name>*</method-name></method>
  
  

第二种用来引用 EJB 的指定的 home 和 component 接口方法:
  <method><ejb-name>EJBNAME</ejb-name><method-name>METHOD</method-name></method>
    
    

如果有多个方法具有相同的重载名称,这种方法将引用所有重载的方法。
第三种风格用来引用具有重载名称的一系列方法里指定方法:
<method><ejb-name>EJBNAME</ejb-name><method-name>METHOD</method-name><method-params><method-param>PARAMETER_1</method-param><!-- ... --><method-param>PARAMETER_N</method-param></method-params></method>
    
    
    
        
        
        
    

这个方法必须在指定的 EJB 的 home 或 remote 接口里进行定义。method-param 元素的值是对应的方法参数类型的全限定名称。如果有多个方法具有相同的重载签名,这些权限将应用到所有匹配的重载方法上。
可选的 method-intf 元素可以用来区分 EJB 里在 home 和 remote 接口都定义了且具有相同名称和签名的方法。
例 2.5 “解释 method-permission 元素用法的 ejb-jar.xml 描述符片段。” 提供了关于 method-permission 用法的完整示例。

例 2.5. 解释 method-permission 元素用法的 ejb-jar.xml 描述符片段。

<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 AardvarkPayroll bean </description><role-name>employee</role-name><method><ejb-name>AardvarkPayroll</ejb-name><method-name>findByPrimaryKey</method-name></method><method><ejb-name>AardvarkPayroll</ejb-name><method-name>getEmployeeInfo</method-name></method><method><ejb-name>AardvarkPayroll</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>
    
        
            
            
            
            
                
                
            
        
        
            
            
            
                
                
            
            
                
                
            
            
                
                
                
                    
                
            
        
        
            
            
            
                
                
            
        
        
            
            
            
                
                
            
        
        
            
            
                
                
            
        
    

2.1.6. EJB 的安全性注解

EJB 使用注解将信息传递给负责应用程序的安全性和其他方面的部署者。部署者可以根据注解或部署描述符里的设置来设立合适的 EJB 安全策略。
在部署描述符里显性指定的任何方法值都会覆盖注解的值。如果在部署描述符里没有指定某个方法的值,那注解的值将被使用。覆盖的颗粒度是以方法为基础的。
EJB 里定义安全性的注解包括:
@DeclareRoles
它指定了代码里声明每个安全角色。关于配置角色的信息,请参考 Java EE 5 TutorialDeclaring Security Roles Using Annotations
@RolesAllowed@PermitAll@DenyAll
它指定注解的方法权限。关于配置注解的方法权限的信息,请参考 Java EE 5 TutorialSpecifying Method Permissions Using Annotations
@RunAs
它配置组件的传播安全标识符。关于使用注解配置传播安全标识符的信息,请参考 Java EE 5 TutorialConfiguring a Component’s Propagated Security Identity

2.1.7. Web 内容安全约束

在 Web 应用程序里,安全性是通过角色来定义的。这些角色根据确定被保护内容的 URL 模式来设置访问权限。这一系列信息是使用 web.xmlsecurity-constraint 元素来声明的。
对 Web 内容安全约束的解释

图 2.5. Web 内容安全约束

要保护的内容是使用一个或多个 <web-resource-collection> 元素来声明的。每个 <web-resource-collection> 元素都包含一个可选的 <url-pattern> 元素序列以及一个 <http-method> 元素序列。<url-pattern> 元素指定访问受保护内容的请求必须匹配的 的 URL 模式,而 <http-method> 元素则指定所允许的 HTTP 请求类型。
可选的 <user-data-constraint> 元素指定了对客户到服务器连接的传输层的要求。这些要求可能用于内容的完整性(防止数据在通讯过程中被篡改)或用于保密性(在传输时防止被读取)。<transport-guarantee> 元素指定了保护客户和服务器间通讯的程度。它的值是 NONEINTEGRALCONFIDENTIALNONE 表示应用程序不要求任何传输保证。INTEGRAL 表示应用程序要求客户和服务器间发送的数据以传输中不可以更改的方式进行。CONFIDENTIAL 表示应用程序要求以防止其他实体观察传输内容的方式进行传输。大多数情况下,INTEGRALCONFIDENTIAL 标记的出现都表示要求使用 SSL。
可选的 <login-config> 元素被用来配置应该使用的验证方法、安全区的名称以及表单登录机制所需的属性。
对 Web 登录配置的解释

图 2.6. Web 登录配置

<auth-method> 子元素指定了 Web 应用程序的验证机制。作为访问任何被授权约束保护的 web 资源的前提条件,用户必须使用配置机制进行验证。合法的 <auth-method> 值为 BASICDIGESTFORMCLIENT-CERT<realm-name> 子元素指定了用于 HTTP 基本和摘要模式授权的安全区名。<form-login-config> 子元素指定了用表单登录的登录页面的错误页面。如果 <auth-method> 值不是FORM,那么 form-login-config 及其子元素将被忽略。
下面的配置示例指定了 Web 应用程序的 /restricted 路径下的任何 URL 都要求 AuthorizedUser 角色。这里不要求传输保证,用于获取用户标识符的验证方式是基本(BASIC)HTTP 验证模式。

例 2.6. web.xml 描述符文件的片段

<web-app>
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Secure Content</web-resource-name>
            <url-pattern>/restricted/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>AuthorizedUser</role-name>
        </auth-constraint>
        <user-data-constraint>
            <transport-guarantee>NONE</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
    <!-- ... -->
    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>The Restricted Zone</realm-name>
    </login-config>
    <!-- ... -->
    <security-role>
        <description>The role required to access restricted content </description>
        <role-name>AuthorizedUser</role-name>
    </security-role>
</web-app>

2.1.8. 启用基于表单的验证

基于表单的验证提供了使用自定义 JSP/HTML 页面的灵活性,它可以定义登录页面、单独的在登录出错时重定向的页面。
基于表单的验证是在部署描述符 web.xml<login-config> 元素里包含 <auth-method>FORM</auth-method> 来实现的。登录和错误页面也在 <login-config> 里定义:
<login-config>
  <auth-method>FORM</auth-method>
  <form-login-config>
    <form-login-page>/login.html</form-login-page>
    <form-error-page>/error.html</form-error-page>
  </form-login-config>
</login-config>
当使用表单验证的 web 应用程序被部署时,Web 容器将使用 FormAuthenticator 把用户引到合适的页面。JBoss EAP 维护了一个会话池,所以并不需要为每个请求准备验证信息。当 FormAuthenticator 接收到一个请求时,它会向 org.apache.catalina.session.Manager 查询现有的会话。如果没有会话存在,新的会话将被创建。FormAuthenticator 然后再检验这个会话的凭证。

注意

每个会话都用 Session ID 来标识,这是一个随机生成的 16 个字节的字符串。在默认情况下,这些值是从 /dev/urandom (Linux) 获取并用 MD5 进行哈希加密的。对 Sesion ID 生成进行检查可以确保 ID 的唯一性。
在经过检验后,这个 Session ID 将作为 cookie 的一部分进行分配,然后返回给客户。这个 cookie 将在随后的客户请求里出现并用于确认用户会话。
传递给客户的 cookie 是一个带有几个可选属性的名称/值对。标识符属性名为 JSESSIONID。它的值是 Session ID 的十六进制字符串。这个 cookie 被配置成非持久性的。这意味着当浏览器退出时它不能在客户端删除。在服务器端,会话将在 60 秒不活动后过期,此时会话对象和凭证信息会被删除。
假设某个用户试图访问用基于表单验证保护的 Web 应用程序。FormAuthenticator 将捕获这个请求,如果有需要则创建一个新的会话,并重定向用户到 login-config 里定义的登录页面。(在之前的代码示例里,登录页面是 login.html)。然后这个用户在 HTML 表单里输入用户名和密码。用户名和密码将通过 j_security_check 表单动作传入 FormAuthenticator
FormAuthenticator 然后根据附加到 Web 应用程序上下文的安全区验证用户名和密码。在 JBoss EAP 里,安全区是 JBossWebRealm。当验证成功后,FormAuthenticator 从缓存里提取保存的请求并将用户重定向到原始的请求。

注意

服务器只有在 URI 以 /j_security_check 结尾,且至少存在 j_usernamej_password 参数时才会识别表单验证请求。

2.1.9. 启用声明式安全性

到目前为止涵盖的 Java EE 安全性元素只是从应用程序的角度描述安全性要求。因为 Java EE 安全性元素声明了逻辑角色,应用程序部署者将这些角色从应用程序域映射到部署环境。Java EE 规格忽略了这些应用服务器专有的细节。
要将应用程序角色映射到部署环境,你必须用 JBoss EAP 专有的部署描述符来指定实现 Java EE 安全模型的安全管理者。关于相关的安全性配置细节,请参考自定义登录模块示例。

第 3 章 JAAS 简介

3.1. 关于 JAAS

JBossSX 框架是基于 JAAS API 的。在理解 JBossSX 的实现细节之前,你必须理解 JAAS API 的基本元素。下列章节提供了对 JAAS 的介绍,可让你为本指南后面的 JBossSX 架构讨论做好准备。
JAAS 1.0 API 由一系列用于用户验证和授权的 Java 软件包组成。这个 API 实现了标准可插拔验证模块(PAM)的 Java 版本且扩展了 Jav 2 平台访问控制架构以支持基于用户的授权。
JAAS 最先是作为 JDK 1.3 的扩展软件包发布的,之后的 JDK 1.5 捆绑了它。因为 JBossSX 框架只使用了 JAAS 的验证功能来实现声明式的基于角色的 J2EE 安全模型,这里就只着重于这个方面的内容。
JAAS 验证是以可插拔的方式执行的。这允许 Java 应用程序对底层的验证技术保持独立,并允许JBossSX 安全管理者在不同的安全基础结构里运行。和安全基础结构的集成不需要修改JBossSX 安全管理者实现就可以实现。你只需要修改 JAAS 所使用的验证配置。

3.2. JAAS 核心类

JAAS 核心类可以分成三个类别:常用类、验证类和授权类。下面的列表只列出了常用类和验证类,因为它们是用于实现本章涵盖的JBossSX 功能的专有类。
这些是通用类:
  • Subject (javax.security.auth.Subject)
这些是验证类:
  • Configuration (javax.security.auth.login.Configuration)
  • LoginContext (javax.security.auth.login.LoginContext)
这些是相关联的接口:
  • Principal (java.security.Principal)
  • Callback (javax.security.auth.callback.Callback)
  • CallbackHandler (javax.security.auth.callback.CallbackHandler)
  • LoginModule (javax.security.auth.spi.LoginModule)

3.3. Subject 和 Principal 类

要授权对资源的访问,应用程序必须首先验证请求的来源。JAAS 框架定义了术语“Subject”来表示请求的来源。Subject 类是 JAAS 里的核心类。Subject 代表单个实体的信息,如某个人或服务。它包含了实体的 principal 、公共凭证和私有凭证。JAAS API 使用现有的 Java 2 java.security.Principal 接口来代表 principal,而它基本上一个类型化名称。
在验证过程中,subject 将用关联的标识符(identiy)或主体(principal)来填充。Subject 可能具有多个主体。例如,某个人可能有名称主体(John Doe)、社会保险号码主体(123-45-6789)以及用户名主体(johnd)),这些都有助于将这个 subject 和其他 subject 区分开来。为了获取和 subject 相关联的主体,你可以使用下列两种方法:
public Set getPrincipals() {...}
public Set getPrincipals(Class c) {...}
getPrincipals() 返回所有包含在主题里的主体。getPrincipals(Class c) 只返回那些作为 c 类或其子类实例的主体。如果这个主题没有匹配的主体则返回空集。
请注意,java.security.acl.Group 接口是 java.security.Principal 的一个子接口,所以主体集里的实例可能代表其他主体或主体组的逻辑分组。

3.4. 主题(Subject)验证

主题验证要求 JAAS 登录。登录过程由下列要点组成:
  1. LoginModule 配置所要求的,用程序实例化一个 LoginContext 并传入登录配置的名称和 CallbackHandler 来填充 Callback 对象。
  2. LoginContext 咨询 Configuration 来加载包括在命名登录配置里的所有 LoginModules。如果不存在这个名称的配置,那么默认会使用 other 配置。
  3. 应用程序调用 LoginContext.login 方法。
  4. 登录模块调用所有加载的 LoginModule。每个 LoginModule 都会试图验证这个主题,它调用相关联的 CallbackHandler 上的 handle 犯法来获取验证过程所需的信息。这些信息以 Callback 对象队列的方式被传入 handle 方法。成功后,LoginModule 将主题和相关的主体(principal)和凭证关联。
  5. LoginContext 返回验证状态给应用程序。从 login 方法返回则表示成功。如果 login 方法抛出异常则表示失败。
  6. 如果验证成功,应用程序使用 LoginContext.getSubject 方法获取已验证的主题。
  7. 在主题验证的作用域完成后,所有的主体以及和 login 方法的主题相关的信息都可以通过调用 LoginContext.logout 方法进行删除。
LoginContext 类提供了用于验证主题的基本方法以及开发独立于底层验证技术的应用程序的途径。LoginContext 咨询 Configuration 来决定为特定应用程序配置的验证服务。LoginModule 类代表验证服务。因此,你可以插入不同的等录模块到应用程序而无需修改应用程序自身。下面的步骤显示了应用程序验证主题所需的步骤。
CallbackHandler handler = new MyHandler();
LoginContext lc = new LoginContext("some-config", handler);

try {
    lc.login();
    Subject subject = lc.getSubject();
} catch(LoginException e) {
    System.out.println("authentication failed");
    e.printStackTrace();
}
                        
// Perform work as authenticated Subject
// ...

// Scope of work complete, logout to remove authentication info
try {
    lc.logout();
} catch(LoginException e) {
    System.out.println("logout failed");
    e.printStackTrace();
}
                        
// A sample MyHandler class
class MyHandler 
    implements CallbackHandler
{
    public void handle(Callback[] callbacks) throws
        IOException, UnsupportedCallbackException
    {
        for (int i = 0; i < callbacks.length; i++) {
            if (callbacks[i] instanceof NameCallback) {
                NameCallback nc = (NameCallback)callbacks[i];
                nc.setName(username);
            } else if (callbacks[i] instanceof PasswordCallback) {
                PasswordCallback pc = (PasswordCallback)callbacks[i];
                pc.setPassword(password);
            } else {
                throw new UnsupportedCallbackException(callbacks[i],
                                                       "Unrecognized Callback");
            }
        }
    }
}
开发者通过创建 LoginModule 接口的实现集成验证技术。这允许管理员插入不同的验证技术到应用程序里。你可以将多个 LoginModule 链接在一起来允许多种验证技术来参与验证过程。例如,一个 LoginModule 可以执行基于用户/密码的验证,而另外一个则可以连接硬件设备如智能卡读写器或生物特征识别器。
LoginModule 的生命周期是由客户创建和发行 login 方法所根据的 LoginContext 对象驱动的。这个过程由两阶段组成:
  • LoginContext 使用其公共的 no-arg 构造器创建每个已配置的 LoginModule
  • 每个 LoginModule 都是通过其 initialize 方法来初始化的。Subject 参数需保证是非 null 的。initialize 方法的签名是:public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)
  • login 被调用来启动验证过程。例如,某个方法实现可能提示用户输入用户名和密码,然后根据存储在命名服务(如 NIS 或 LDAP)里的数据进行检验。其他的实现可能连接智能卡和生物设备,或者简单地从底层操作系统抽取信息。每个 LoginModule 对用户标识符的检验会在 JAAS 验证的第一阶段来考虑。login 方法的签名是 boolean login() throws LoginExceptionLoginException 表示失败。返回值为 true 表示方法调用成功,而 false 表示登录模块应该被忽略。
  • 如果 LoginException 的总体验证成功,commit 将在每个 LoginModule 上调用。如果 LoginModule 的第一阶段验证成功,commit 方法将继续第二阶段并将相关的主体、公共凭证和/或私有凭证和关联主题。如果 LoginModule 的第一阶段失败,那 commit 将删除任何之前保存的验证状态,如用户名或密码。commit 方法的签名是:boolean commit() throws LoginExceptionLoginException 的抛出表示完成提交阶段失败。返回值为 true 表示方法调用成功,而 false 表示登录模块应该被忽略。
  • 如果 LoginException 的总体验证失败,abort 将在每个 LoginModule 上调用。abort 方法删除或销毁任何之前 login 或 initialize 方法创建的验证状态。abort 方法的签名是:boolean abort() throws LoginExceptionLoginException 的抛出表示完成 abort 阶段失败。返回值为 true 表示方法调用成功,而 false 表示登录模块应该被忽略。
  • 要在成功登录后删除验证状态,应用程序将调用 LoginContext 上的 logout。这会导致在每个 LoginModule 上调用 logout 方法。logout 方法删除原来在 commit 阶段和主题相关联的主体和凭证。凭证应该在删除时被销毁。logout 方法的签名是:boolean logout() throws LoginExceptionLoginException 的抛出表示完成登出阶段失败。返回值为 true 表示方法调用成功,而 false 表示登录模块应该被忽略。
LoginModule 必须和用户进行通讯来获取验证信息时,它使用 CallbackHandler 对象。应用程序实现 CallbackHandler 接口并将其传入 LoginContext,这会直接发送验证信息到底层的等录模块。
登录模块使用 CallbackHandler 来获取用户的输入,如密码或智能卡 PIN,并提供信息给用户,如状态信息。通过允许应用程序指定 CallbackHandler,底层的 LoginModule 保持对应用程序和用户交互的独立。例如,GUI 应用程序的 CallbackHandler 实现可能显示一个窗口来让用户输入。另一方面,非 GUI 的应用程序可能 CallbackHandler 简单地使用应用服务器的 API 来获取凭证信息。 CallbackHandler 接口有一个方法需要被实现:
void handle(Callback[] callbacks)
    throws java.io.IOException, 
           UnsupportedCallbackException;
Callback 接口是我们最后将了解的验证类。它是为几个默认实现提供的一个标记接口,其中包括 NameCallback 和在以前示例里使用的 PasswordCallbackLoginModule 使用 Callback 来请求验证机制所需的信息。在验证的登录阶段,LoginModule 将一个 Callback 队列直接传入 CallbackHandler.handle 方法。如果 callbackhandler 无法理解如何使用传递到 handle 方法的 Callback,它会抛出 UnsupportedCallbackException 来中止 login 的调用。

部分 II. 保证平台的安全

第 4 章 安全子系统

4.1. 关于安全子系统

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

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

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

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

安全域

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

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

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

例 4.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> 元素。

4.3. 配置安全子系统

4.3.1. 配置安全子系统

你可以用管理 CLI 或基于 web 的管理控制台来配置安全子系统。
安全子系统里的每个顶级元素都包含关于安全配置不同方面的信息。关于安全子系统配置的例子,请参考 第 4.2 节 “关于安全子系统的结构”
<security-management>
这部分内容覆盖了安全子系统的高层行为。每个设置都是可选的。除了 Deep Copy 模式,须该这些设置的任何一个都是不寻常的。
选项 描述
deep-copy-subject-mode
指定是否复制或链接安全令牌以用于额外的线程安全。
authentication-manager-class-name
指定一个要使用的其他的 AuthenticationManager 实现的类名。
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 类上设置的属性的名字和值。

4.3.2. 安全管理

4.3.2.1. 关于 Deep Copy Subject 模式

如果 Deep Copy 主题模式被禁用(默认),复制安全数据结构会产生一个对原始结构的引用,而不是复制整个数据结构。这个行为效率更高,但在具有相同标识符的多个线程通过冲刷或登出操作清除主题时容易受数据损坏的影响。
只要被标记为可克隆的(cloneable),Deep Copy 主题模式会导致数据结构及相关数据的完整复制。这是更多线程安全的,但效率更低。
Deep Copy Subject 模式是作为安全子系统的一部分来配置的。

4.3.2.2. 启用 Deep Copy Subject 模式

你可以通过基于 Web 的管理控制台或管理 CLI 来启用 Deep Copy 安全模式。

过程 4.1. 通过管理控制台启用 Deep Copy 安全模式

  1. 登录到管理控制台。

    管理控制台通常可通过类似 http://127.0.0.1:9990/ 的 URL 进行访问,请根据需要调整。
  2. 受管域:选择合适的配置集。

    在受管域里,安全子系统是针对每个配置集进行配置的,而且你可以在每个子系统里独立地启用或禁用 Deep Copy 安全模式。
    要选择配置集,请点击控制台右上角的 Profiles 标签,然后在左上角的 Profile 选择你要修改的配置集家。
  3. 打开 Security Subsystem 配置菜单。

    展开管理控制台右侧的 Security 菜单,然后点击 Security Subsystem 链接。
  4. 修改 deep-copy-subject-mode 值。

    点击 Edit 按钮。选定 Deep Copy Subjects: 复选框来启用 Deep Copy Subject 模式。
使用管理 CLI 启用 Deep Copy Subject 模式

如果你想通过管理 CLI 来启用这个选项,请使用下列命令。

例 4.2. 受管域

/profile=full/subsystem=security:write-attribute(name=deep-copy-subject-mode,value=TRUE)

例 4.3. 独立服务器

/subsystem=security:write-attribute(name=deep-copy-subject-mode,value=TRUE)

4.3.3. 安全域

4.3.3.1. 关于安全域

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

4.3.3.2. 关于 Picketbox

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

第 6 章 Java 安全性管理者

6.1. 关于 Java 安全性管理者

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

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

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

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

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

前提条件

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

过程 6.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. 启动域或服务器。

    正常地启动域或服务器。

6.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“。
Permissions
赋予代码的权限。许多权限是作为 Java EE 规格的一部分提供的。本文档只涵盖由 JBoss EAP 6 提供的其他权限。

过程 6.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. 删除现有的策略。

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

JBoss EAP 6 专有的权限

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.SecurityAssociationpushRunAsRolepopRunAsRolepushRunAsIdentitypopRunAsIdentity 方法的访问。使用这个运行时权限涉及的风险是可以修改当前调用者的 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)

6.5. 调试安全管理者策略

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

过程 6.3. 启用普通调试

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

    在服务器配置文件里添加下列行。
    • 如果 JBoss EAP 6 实例运行在受管域里,这一行将添加到 bin/domain.conf()或 bin/domain.conf.bat(Windows)。
    • 如果 JBoss EAP 6 实例作为独立服务器运行,这一行将添加到 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"
结果

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

第 7 章 安全区

7.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.properties 存储用户和角色的映射。
属性文件存储在 domain/configuration/standalone/configuration/ 目录里。这些文件由 add-user.shadd-user.bat 同时写入。当你运行这命令时,你的第一个决定是添加新用户到哪个区。

7.2. 添加新的安全区

  1. 运行管理 CLI。

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

    运行下列命令在域控制器或独立服务器上创建一个名为 MyDomainRealm 的安全区。
    /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)
结果

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

7.3. 添加用户到安全区里

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

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

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

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

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

第 8 章 加密

8.1. 关于加密

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

8.2. 关于 SSL 加密

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

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

简介

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

前提条件

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

注意

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

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

  1. 添加新的 HTTPS 连接器。

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

    例 8.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/

    例 8.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 属性参数的完整列表,请参考 第 8.5 节 “SSL 连接器引用”
  3. 部署应用程序。

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

8.4. 生成 SSL 密钥和证书

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

前提条件

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

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

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

    运行下列命令来当前目录里生成带有别名 jboss 的名为server.keystore 的密钥库。
    keytool -genkeypair -alias jboss -keyalg RSA -keystore server.keystore -storepass mykeystorepass --dname "CN=jsmith,OU=Engineering,O=mycompany.com,L=Raleigh,S=NC,C=US"
    下表描述了用于 keytool 命令的参数:
    参数 描述
    -genkeypair 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 服务器通讯。

8.5. SSL 连接器引用

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

表 8.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 6 的用户的主目录下的 .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
用于信任库和密钥库的密码。在下面的例子里,请用自己的密码替换 PASSWORD
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=password,value=PASSWORD)
protocol
要使用的 SSL 协议的版本。支持的值包括 SSLv2SSLv3TLSv1SSLv2+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
用于密钥库里的服务器证书的别名。在下面的例子里,请用你的证书别名替换 KEY_ALIAS
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=key-alias,value=KEY_ALIAS)
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 的证书密码。在下面的例子里,请用自己的掩码密码替换其中的 MASKED_PASSWORD
/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 会话缓存的大小。这个属性仅用于 JSSE 连接器。默认值是 0,它表示缓存大小是无限的。
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=session-cache-size,value=100)
session-timeout
在缓存的 SSLSession 过期前的秒数。这个属性仅适用于 JSSE 连接器。默认值为 86400 秒,也就是 24 小时。
/profile=default/subsystem=web/connector=HTTPS/ssl=configuration/:write-attribute(name=session-timeout,value=43200)

8.6. 兼容 FIPS 140-2 的加密

8.6.1. 关于 FIPS 140-2 兼容性

联邦信息处理标准 140-2 ( Federal Information Processing Standard,FIPS 140-2) 是鉴定加密软件模块的美国政府计算机安全标准。FIPS 140-2 兼容性通常是政府机构和私有企业使用的软件系统的要求之一。
JBoss EAP 6 使用了外部的模块加密,你可以配置它使用兼容 FIPS 140-2 的加密模块。

8.6.2. 兼容 FIPS 140-2 的密码

兼容 FIPS 的密码必须具有下列特征:
  1. 其长度必须至少有 7 个字符。
  2. 必须包含来自至少三个下列字符类别里的字符:
    • ASCII 数字
    • 小写的 ASCII 字符
    • 大写的 ASCII 字符
    • 非字母的 ASCII,和
    • 非 ASCII 字符
如果第一个字符是大写的 ASCII 字符,它不会算作限制 2 里的大写 ASCII 字符。
如果密码的最后一个字符是 ASCII 数字,它不会算作限制 2 里的 ASCII 数字。

8.6.3. 在红帽企业版 Linux 6 上启用 SSL 的 FIPS 140-2 加密

这个任务描述了如何配置 JBoss EAP 6 的 Web 容器(JBoss Web)的 SSL 为兼容 FIPS 140-2 的加密。这个任务只涵盖在红帽企业版 Linux 6 上实现的步骤。
为了这个功能,这个任务使用了 FIPS 模式的 Mozilla NSS 库。

前提条件

过程 8.3. 启用 SSL 的 FIPS 140-2 兼容加密

  1. 创建数据库

    jboss 用户拥有的一个目录里创建 NSS 数据库。
    $ mkdir -p  /usr/share/jboss-as/nssdb
    $ chown jboss /usr/share/jboss-as/nssdb 
    $ modutil -create -dbdir /usr/share/jboss-as/nssdb
    
  2. 创建 NSS 配置文件

    /usr/share/jboss-as 目录里创建一个名为 nss_pkcsll_fips.cfg 的文本文件,它具有下列内容:
    name = nss-fips
    nssLibraryDirectory=/usr/lib64
    nssSecmodDirectory=/usr/share/jboss-as/nssdb
    nssModule = fips
    
    NSS 配置文件必须指定:
    • 名称
    • NSS 库所在的目录,和
    • 步骤 1 里创建 NSS 数据库用到的目录。
    如果你使用的是红帽企业版 Linux 6 的 64 位版本,请设置 nssLibraryDirectory/usr/lib 而不是 /usr/lib64
  3. 启用 SunPKCS11 供应商

    编辑你的 JRE 的java.security 配置文件( $JAVA_HOME/jre/lib/security/java.security)并添加下列行:
    security.provider.1=sun.security.pkcs11.SunPKCS11  /usr/share/jboss-as/nss_pkcsll_fips.cfg
    
    请注意这一行里指定的配置文件就是我们在步骤 2 里创建的文件。
    这个文件里的任何其他 security.provider.X 行都必须将 X 递增以设置对应供应商的优先级。
  4. 为 NSS 库启用 FIPS 模式

    运行 modutil 命令以启用 FIPS 模式:
    modutil -fips true -dbdir /usr/share/jboss-as/nssdb
    请注意这里指定的目录就是我们在步骤 1 里创建的目录。
    此时你可能遇到一个安全库错误,要求你为某些 NSS 共享对象重新生成库签名。
  5. 修改 FIPS 令牌的密码

    用下列命令在 FIPS 令牌上设置密码。请注意,令牌的名称必须为 NSS FIPS 140-2 Certificate DB
    modutil -changepw "NSS FIPS 140-2 Certificate DB" -dbdir /usr/share/jboss-as/nssdb
    用于 FIPS 令牌的密码必须是兼容 FIPS 的密码。
  6. 使用 NSS 工具创建证书

    输入下列命令来用 NSS 工具创建证书。
    certutil -S -k rsa -n jbossweb  -t "u,u,u" -x -s "CN=localhost, OU=MYOU, O=MYORG, L=MYCITY, ST=MYSTATE, C=MY" -d /usr/share/jboss-as/nssdb
  7. 配置 HTTPS 连接器使用 PKCS11 密钥库

    用 JBoss CLI 工具里的下列命令添加一个 HTTPS 连接器:
    /subsystem=web/connector=https/:add(socket-binding=https,scheme=https,protocol=HTTP/1.1,secure=true)
    
    然后用下列命令添加 SSL 配置,用兼容 FIPS 的密码(参考步骤 5)替换这里的 PASSWORD。
    /subsystem=web/connector=https/ssl=configuration:add(name=https,password=PASSWORD,keystore-type=PCKS11,
    cipher-suite="SSL_RSA_WITH_3DES_EDE_CBC_SHA,SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
    TLS_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
    TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,
    TLS_DHE_DSS_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
    TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
    TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
    TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
    TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
    TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,TLS_ECDH_anon_WITH_AES_128_CBC_SHA,
    TLS_ECDH_anon_WITH_AES_256_CBC_SHA")
    
  8. 检验

    运行下列命令检验 JVM 是否可以从 PKCS11 密钥库楼里读取私有密钥:
    keytool -list -storetype pkcs11
    

例 8.3. 使用 FIPS 140-2 的 HTTPS 连接器的 XML 配置

<connector name="https" protocol="HTTP/1.1" scheme="https" socket-binding="https" secure="true">
  <ssl name="https" password="****" 
      cipher-suite="SSL_RSA_WITH_3DES_EDE_CBC_SHA,SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
         TLS_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
         TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,
         TLS_DHE_DSS_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
         TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
         TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
         TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
         TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
         TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
         TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
         TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,TLS_ECDH_anon_WITH_AES_128_CBC_SHA,
         TLS_ECDH_anon_WITH_AES_256_CBC_SHA"
      keystore-type="PKCS11"/>
</connector>
请注意,cipher-suite 属性里插入了断行符以便于阅读。

第 9 章 网络安全性

9.1. 保护管理接口

总结

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

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

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

概述

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

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

警告

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

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

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

    例 9.1. 指定公共接口。

    EAP_HOME/bin/domain.sh -b 10.1.1.1

    例 9.2. 指定管理接口。

    EAP_HOME/bin/domain.sh -bmanagement=10.1.1.1

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

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

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

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

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

总结

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

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

前提条件

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

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

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

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

  1. 登录到管理控制台。

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

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

    注意

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

    根据特定端口的功能以及系统环境的要求,你可能需要在防火墙上打开某些端口。
  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 服务器上配置防火墙,并关闭非操作必需的端口。请参考 第 9.4 节 “JBoss EAP 6 使用的网络端口” 并确定要打开的端口,然后关闭其他端口。红帽企业版 6 的默认配置关闭所有的端口,除了 22(用于 SSH)和 5353(用于多点传送 DNS)。当你在配置端口的时候,请确保你可以从物理上访问服务器,这样就不会不经意地将自己关在外面。
结果

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

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

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

注意

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

默认的套接字绑定组

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

表 9.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 6 和 HTTP 加载平衡器之间通讯的多点传送端口。
osgi-http 8090 由使用 OSGi 子系统的内部组件使用。不能通过管理界面进行配置。
remoting 4447 用于远程 EJB 调用。
txn-recovery-environment 4712 JTA 事务恢复管理者。
txn-status-manager 4713 JTA / JTS 事务管理者。
管理端口

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

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

第 10 章 管理接口的安全性

10.1. 保护管理接口

总结

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

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

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

简介

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

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

    注意

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

10.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 6 的用户的验证和授权。

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

注意

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

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

管理接口

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

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

10.4. 禁用 HTTP 管理接口

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

注意

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

例 10.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 接口,请执行下列命令:

例 10.2. 删除 HTTP 接口

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

例 10.3. 重新创建 HTTP 接口

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

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

总结

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

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

例 10.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>

前提条件

  • 启动 JBoss EAP 6 实例。
  • 启动管理 CLI。

过程 10.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 里删除了。

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

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

注意

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

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

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

例 10.6. 删除 JMX 子系统

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

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

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

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

例 10.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 的安全区并为相关的配置文件设置了目录。

例 10.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)

应用安全区到管理接口里

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

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

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

10.8. 为独立模式下的管理控制台配置 HTTPS

过程 10.2. 

  1. 通过添加 management-https 配置并删除 management-http 配置,确保管理控制台捆绑了 HTTPS
    这可以通过编辑 standalone.xml 文件(我们不推荐这么做)或者使用下列 CLI 接口命令来完成:
    /core-service=management/management-interface=http-interface:write-attribute(name=secure-socket-binding, value=management-https)
    /core-service=management/management-interface=http-interface:undefine-attribute(name=socket-binding)
  2. 可选的:

    如果你使用了自定义的 socket-binding 组,请确保已定义了 management-https(这是默认选项,绑定了端口 9443)。
     <socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
            <socket-binding name="management-native" interface="management" port="${jboss.management.native.port:9999}"/>
            <socket-binding name="management-http" interface="management" port="${jboss.management.http.port:9990}"/>
            <socket-binding name="management-https" interface="management" port="${jboss.management.https.port:9443}"/>
    
    
  3. 按照 第 8.4 节 “生成 SSL 密钥和证书” 里讨论的步骤生成密钥对。
  4. standalone.xml 配置文件的 security-realm 部分添加一个 server-identities 元素。
    在这个元素里,你要定义用于密钥对的密钥库的路径、密钥库的密码和别名。
    执行下列 CLI 命令,请用自己的值来替换例子里的值。这个例子假设密钥库被复制到服务器的配置目录,对于独立服务器来说,也就是 EAP_HOME/standalone/configuration/
    /core-service=management/security-realm=ManagementRealm/server-identity=ssl:add(keystore-path=server.keystore,keystore-relative-to=jboss.server.config.dir, keystore-password=SECRET, alias=KEY_ALIAS)
  5. 重启你的独立服务器。

10.9. 为域模式下的管理控制台配置 HTTPS

过程 10.3. 

  1. 按照 第 8.4 节 “生成 SSL 密钥和证书” 里讨论的步骤生成密钥对。
  2. host.xml. 配置文件的 security-realm 部分添加一个 server-identities 元素。
    在这个元素里,你要定义用于密钥对的密钥库的路径、密钥库的密码和别名。
    执行下列 CLI 命令,请用自己的值来替换例子里的值。这个例子假设密钥库被复制到服务器的配置目录,对于受管域来说,也就是 EAP_HOME/domain/configuration/ 。
    /host=master/core-service=management/security-realm=ManagementRealm/server-identity=ssl:add(protocol=TLSv1, keystore-path=server.keystore,keystore-relative-to=jboss.domain.config.dir, keystore-password=SECRET, alias=KEY_ALIAS)
  3. 通过添加 secure-port 配置并删除端口配置,修改 management-interface 部分里 socket 元素。
    请使用下列名:
    /host=master/core-service=management/management-interface=http-interface:write-attribute(name=secure-port,value=9443) 
    /host=master/core-service=management/management-interface=http-interface:undefine-attribute(name=port)
  4. 重启你的域。

10.10. 对管理接口和 CLI 使用双向 SSL

在这个主题里,我们将使用下列约定:

HOST1
JBoss 服务器的主机名。例如,jboss.redhat.com
HOST2
客户的名称。例如 myclient。请注意,这并不需要实际的主机名。
CA_HOST1
用于 HOST1 证书的标识名(DN)。例如 cn=jboss,dc=redhat,dc=com
CA_HOST2
用于 HOST2 证书的标识名(DN)。例如 cn=myclient,dc=redhat,dc=com

过程 10.4. 

  1. 生成库:
    keytool -genkeypair -alias HOST1_alias -keyalg RSA -keysize 1024 -validity 365 -keystore host1.keystore.jks -dname "CA_HOST1" -keypass secret -storepass secret
    keytool -genkeypair -alias HOST2_alias -keyalg RSA -keysize 1024 -validity 365 -keystore host2.keystore.jks -dname "CA_HOST2" -keypass secret -storepass secret
  2. 导出证书:
    keytool -exportcert  -keystore HOST1.keystore.jks -alias HOST1_alias -keypass secret -storepass secret -file HOST1.cer
    
    keytool -exportcert  -keystore HOST2.keystore.jks -alias HOST2_alias -keypass secret -storepass secret -file HOST2.cer
    
  3. 将证书导入到对应的信任库:
    keytool -importcert -keystore HOST1.truststore.jks -storepass secret -alias HOST2_alias -trustcacerts -file HOST2.cer
    
    keytool -importcert -keystore HOST2.truststore.jks -storepass secret -alias HOST1_alias -trustcacerts -file HOST1.cer
    
  4. 在配置文件(host.xmlstandalone.xml)里定义一个 CertificateRealm 并让接口指向它:
    这可以通过编辑配置文件文件(我们不推荐这么做)或者使用下列命令来完成:
    /core-service=management/security-realm=CertificateRealm:add()
    /core-service=management/security-realm=CertificateRealm:add/server-identity=ssl:add(keystore-path=/path/to/HOST1.keystore.jks,keystore-password=secret, alias=HOST1_alias)
    /core-service=management/security-realm=CertificateRealm/authentication=truststore:add(keystore-path=/path/to/HOST1.truststore.jks,keystore-password=secret)
  5. 编辑 JBOSS_HOME/bin/jboss-cli.xml 并添加 SSL 配置(使用合适的值):
    <ssl>
      <alias>$HOST2alias</alias>
      <key-store>/path/to/HOST2.keystore.jks</key-store>
      <key-store-password>secret</key-store-password>
      <trust-store>/path/to/HOST2.truststore.jks</trust-store>
      <trust-store-password>secret</trust-store-password>
      <modify-trust-store>true</modify-trust-store>
    </ssl>
    
    

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

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

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

警告

我们在 JBoss EAP 6 里使用库安全功能时遇到了问题。在使用 IBM JDK 时,我们发现 vault.keystore 生成的 Sun/Oracle keytool 不是有效的密钥库。这是由于不同的 Java 供应商里 JCEKS 密钥库实现是不同的。
当 Oracle Java 生成的密钥库用于 IBM Java 上的 JBoss EAP 实例时就会出现这个问题。此时服务器不会启动并抛出如下异常:
java.io.IOException: com.sun.crypto.provider.SealedObjectForKeyProtector
目前,唯一的变通办法是在使用 IBM Java 实现的环境里避免用 Oracle keytool 来生成密钥库。

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

前提条件

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

过程 10.5. 设置 Java 密钥库

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

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

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

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

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

    $ keytool -genseckey -alias vault -storetype jceks -keyalg AES -keysize 128 -storepass vault22 -keypass vault22 -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 6 保存加密字符串,如密码。

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

前提条件

  1. 运行 vault.sh 命令。

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

    这个目录应该比较安全,但 JBoss EAP 6 需要能够访问它。如果你按照 第 10.11.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 的别名。如果你按照 第 10.11.2 节 “创建一个 Java 密钥库来存储敏感信息” 来创建 vault,那么别名应该是 vault
  5. 退出交互式控制台。

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

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

10.11.4. 配置 JBoss EAP 6 来使用密码库

概述

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

过程 10.6. 设置密码库

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

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

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

    注意

    如果你使用的是 Microsoft Windows 服务器,请用 4 个 \ 字符替换文件或目录里的每个 / 字符。这是因为它需要两个 \ 来作为脱字符。对于其他 / 字符则不需要这么做。
    • 受管域

      /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 6 已配置好通过密码库来破解掩码字符串。要添加字符串到库里并在配置里使用它们,请参考下列主题:第 10.11.5 节 “在 Java 密钥库里保存和获取加密的敏感字符串”

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

总结

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

过程 10.7. 设置 Java 密钥库

  1. 运行 vault.sh 命令。

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

    如果你遵循 第 10.11.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
    Configuration should be done as follows:
    VAULT::ds_ExampleDS::password::1
    ********************************************
    
  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::1}</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::1}</password>
    

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

概述

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

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

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

例 10.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
Configuration should be done as follows:
VAULT::DS::thePass::1
********************************************

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

例 10.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::1",
        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 字符串了。

10.12. LDAP

10.12.1. 关于 LDAP

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

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

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

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

表 10.1. LDAP 转出连接的属性

属性 要求的 描述
url
目录服务器的 URL 地址。
search-dn
授权执行搜索的用户的全限定可区分名称(Distinguished Name,DN)。
search-credentials
用户授权执行搜索的密码。
initial-context-factory
当建立连接时使用的初始上下文。默认为 com.sun.jndi.ldap.LdapCtxFactory
security-realm
为了获得建立连接时所需的已配置的 SSLContext 而引用的安全区。

例 10.13. 添加 LDAP 转出连接

这个例子用下列属性集添加了一个转出连接:
  • 搜索 DN: cn=search,dc=acme,dc=com
  • 搜索凭证: myPass
  • URL: ldap://127.0.0.1:389
第一个命令添加了安全区。
/host=master/core-service=management/security-realm=ldap_security_realm:add
第二个命令添加了 LDAP 连接。
/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")
创建一个启用 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))

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

这个例子使用了下列参数:
  • connection - ldap_connection
  • base-dn - cn=users,dc=acme,dc=com.
  • username-filter - attribute="sambaAccountName"
<security-realm name="ldap_security_realm">
   <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(如果它还没有被设置)。在旧的版本里,这个选项应该手动进行配置。

例 10.15. 添加 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 验证。

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

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

要配置受管域里的主机根据 Microsoft Active Directory 来验证,请按照这个过程来进行,它创建了一个安全域并使用 JAAS 验证映射角色到活动目录组。这个过程是必需的,因为 Microsoft Active Directory 允许用空的密码进行绑定。这个过程防止了在应用程序平台里使用空的密码。

在执行这个过程之前,你需要知道主机控制器的名称。这个例子假定主机控制器名为 master
  1. 添加一个名为 ldap_security_realm 的新的 <security-realm>,并配置它使用 JAAS。

    下列管理 CLI 命令添加了新的安全区并设置了它的验证机制。请按需要修改主机的名称。
    /host=master/core-service=management/security-realm=ldap_security_realm/:add
    /host=master/core-service=management/security-realm=ldap_security_realm/authentication=jaas/:add(name=managementLDAPDomain)
  2. 配置 <http-interface> 以使用这个新的安全区。

    下列管理 CLI 命令配置了 HTTP 接口。
    /host=master/core-service=management/management-interface=http-interface/:write-attribute(name=security-realm,value=ldap_security_realm)
  3. 配置 JBoss EAP,在其启动参数里添加自定义的 JAAS 配置。

    编辑 EAP_HOME/bin/domain.conf 文件。搜索 HOST_CONTROLLER_JAVA_OPTS 变量。这是你添加 JBoss EAP 启动前所需的 JVM 指令的地方。下面是这个参数的默认内容的示例:
    HOST_CONTROLLER_JAVA_OPTS="$JAVA_OPTS"
    
    添加下列指令:-Djava.security.auth.login.config=/opt/jboss-eap-6.0/domain/configuration/jaas.conf"
    被编辑的行类似于:
    -Djava.security.auth.login.config=/opt/jboss-eap-6.0/domain/configuration/jaas.conf"
    
  4. 在模块选项里添加登陆模块。

    在相同的文件里,找到包含下列内容的行:
    JBOSS_MODULES_SYSTEM_PKGS="org.jboss.byteman"
    修改它为下面的样子请确保不要插入任何多余的空格。
    JBOSS_MODULES_SYSTEM_PKGS="org.jboss.byteman,com.sun.security.auth.login"
    保存并关闭 domain.conf 文件。
  5. 创建将添加至 classpath 的 JAAS 配置。

    在下列位置创建一个新的文件:EAP_HOME/domain/configuration/jaas.conf
    这个文件应该包含下列内容。请按照自己的环境编辑相关的参数。
    managementLDAPDomain {
        org.jboss.security.auth.spi.LdapExtLoginModule required
            java.naming.factory.initial="com.sun.jndi.ldap.LdapCtxFactory"
            java.naming.provider.url="ldap://your_active_directory_host:389"
            java.naming.security.authentication="simple"
            bindDN="cn=Administrator,cn=users,dc=domain,dc=your_company,dc=com"
            bindCredential="password"
            baseCtxDN="cn=users,dc=domain,dc=redhat,dc=com"
            baseFilter="(&(sAMAccountName={0})(|(memberOf=cn=Domain Guests,cn=Users,dc=domain,dc=acme,dc=com)(memberOf=cn=Domain Admins,cn=Users,dc=domain,dc=acme,dc=com)))"
            allowEmptyPasswords="false"
            rolesCtxDN="cn=users,dc=domain,dc=acme,dc=com"
            roleFilter="(cn=no such group)"
            searchScope="SUBTREE_SCOPE";
    };
    
  6. 重启 JBoss EAP,你的 HTTP 接口应该使用 LDAP 服务器来验证了。

第 11 章 用基于角色的访问控制来保护管理接口

11.1. 关于基于角色的访问控制(Role-Based Access Control,RBAC)

关于基于角色的访问控制(RBAC)是一个指定管理用户的一系列权限的机制。它允许多个用户共享管理 JBoss EAP 6.2 服务器的职责而不是拥有不受限制的权限。通过对管理用户的“职责分离”,JBoss EAP 6.2 使机构可以轻易地在个人和组之间分配职责,从而避免分配不必要的权限。这既确保了服务器和数据最大可能的安全性,也提供了配置、部署和管理的灵活性。

JBoss EAP 6.2 里的关于基于角色的访问控制是通过角色权限和约束的组合来实现的。

它提供了 7 个预定义的角色,每个都有不同的固定权限。这些预定义的角色是:Monitor、Operator、Maintainer、Deployer、Auditor、Administrator 和 SuperUser。每个管理用户都分配了一个或多个角色,这些角色指定了管理服务器时用户被允许做的事情。

11.2. GUI 和 CLI 的基于角色的访问控制

当启用了基于角色的访问控制(RBAC)时,根据分配的角色,用户将不能运行某些操作、读取某些资源,甚至根本无法查看管理模型的某些部分。
管理控制台

在管理控制台里,根据你分配的角色的权限,有些控件和视图是禁用的(灰色)或不可见的。

如果你没有对某个资源属性的读权限,该权限将在控制台里显示为空白。例如,多数角色无法读取数据源的用户名和密码字段。

如果你没有对某个资源属性的写权限,该权限将在资源编辑表单里显示为禁用(灰色)。如果你根本没有对这个资源的写权限,那么整个编辑表单都不会出现。

如果你没有对某个资源或属性的访问权限(就是对于你的角色来说是“不可寻址的”),那它们根本不会出现在在控制台里。其中一个例子是,访问控制系统自身在默认情况下只对一些角色可见。
管理 API

启用了 RBAC 后,使用 jboss-cli.sh 工具或直接使用 API 的用户在 API 里会遇到稍许不同的行为。

无法读取的资源和属性将从结果里过滤。如果被过滤的内容是角色可以寻址的,那么它们的名称将列为结果里 response-headers 部分的 filtered-attributes。如果它们是无法被寻址的,那根本不会被列出。

试图访问不能寻址的资源将导致 "resource not found" 错误。

如果用户试图写入或读取他们可以寻址的资源但缺乏对应的读写权限,就会返回 "Permission Denied" 。

11.3. 支持的验证模式

基于角色的访问控制(RBAC)可以和 JBoss EAP 6.2 附带的标准验证提供者一起使用。它们是:username/passwordclient certificatelocal user
Username/Password

用户通过用户名和密码组合根据 mgmt-users.properties 文件或 LDAP 服务器来进行检验。
Client Certificate

使用信任库。
Local User

如果服务器运行在相同的服务器上,jboss-cli.sh 将自动验证为本地用户。在默认情况下,本地用户是 SuperUser 组的成员。
不管使用哪个提供者,JBoss EAP 负责将角色分配给用户。然而,当用 mgmt-users.properties 文件或 LDAP 服务器来验证时,这些系统可以提供用户组信息。JBoss EAP 也可以使用这些信息来为用户分配角色。

11.4. 标准角色

JBoss EAP 6 提供 7 个预定义的用户角色:Monitor、Operator、Maintainer、Deployer、Auditor、Administrator 和 SuperUser。每个角色都有不同的一套权限以用于专有的用例。Monitor, Operator, Maintainer, Administrator 和 SuperUser 都是在前者之上构建的,其权限一次递增。Auditor 和 Deployer 角色分别与 Monitor 和 Maintainer 角色类似,但都具有一些其他的特殊权限和限制。
Monitor

充当 Monitor 角色的用户具有最小的权限且只能读取服务器的当前配置和状态。这个角色是为了那些需要跟踪和报告服务器性能的用户而设计的。

Monitor 角色既不能修改服务器配置也不能访问敏感数据或操作。
Operator

Operator 角色扩展了 Monitor 角色,它添加了修改服务器的运行时状态的能力。这表示 Operator 可以重载并关闭服务器,也可以暂停和恢复 JMS 目的地。Operator 角色对于负责应用服务器的物理或虚拟主机的用户是很理想的,他们可以确保有需要时关闭和重启服务器。

Operator 角色既不能修改服务器配置也不能访问敏感数据或操作。
Maintainer

Maintainer 角色可以查看和修改运行时状态以及除了敏感数据和操作之外的所有配置。Maintainer 角色是普通用途的角色,它不能访问敏感数据和操作。Maintainer 角色赋予用户几乎完整的管理服务器的权限,但不能访问密码和其他敏感信息。

Maintainer 角色不能访问敏感数据或操作。
Administrator

Administrator 角色具有对服务器上除了审计日志系统以外的所有资源和操作的无限制的访问权限。 Administrator 是唯一(除了 SuperUser)可以访问敏感数据和操作的角色。这个角色也可以配置访问控制系统。只有处理敏感数据或配置用户和角色时才要求 Administrator 角色。

Administrator 角色不能访问审计日志系统且不能将自己修改为 Auditor 或 SuperUser 角色。
SuperUser

SuperUser 角色没有任何限制,它可以访问任何服务器的资源和操作,包括审计日志系统。这个角色等同于以前的 JBoss EAP 6 版本(6.0 和 6.1)里的管理员用户。如果禁用了 RBAC,所有的管理用户都会拥有和 SuperUser 角色相同的权限。
Deployer

Deployer 角色具有和 Monitor 相同的角色,但它可以修改部署的配置和状态以及其他启用为应用程序资源的资源类型。
Auditor

Auditor 角色具有 Monitor 角色的所有权限,它也可以查看(但不能修改)敏感数据,且具有对审计日志系统的完全权限。Auditor 是除了 SuperUser 之外唯一能够访问审计日志系统的角色。

Auditor 角色不能修改敏感数据或资源。它只有读的权限。

11.5. 关于角色权限

角色具有的权限定义了它能做的事情。并非每个角色都具有每个权限。值得注意的是,SuperUser 具有所有权限,而 Monitor 的权限最小。
每个权限都可以为某个类别的资源赋予读和写权限。
这些类别是:运行时状态、服务器配置、敏感数据、审计日志和访问控制系统。
表 11.1 “角色权限矩阵” 总结了每个角色权限。

表 11.1. 角色权限矩阵

Monitor

Operator

Maintainer

Deployer

Auditor

Administrator

SuperUser

读取配置和状态

X

X

X

X

X

X

X

读取敏感数据 [2]

X

X

X

修改敏感数据 [2]

X

X

读/修改审计日志

X

X

修改运行时状态

X

X

X[1]

X

X

修改持久性配置

X

X[1]

X

X

读/修改访问控制

X

X
[1] 权限限于应用程序资源。
[2] 哪些资源被当作 "敏感数据" 是使用敏感性约束来配置的。

11.6. 关于约束

约束(Constraint)是对于指定资源列表的访问控制配置的集合。RBAC 系统使用了约束和角色权限的组合来确定某个用户是否可以执行管理动作。

约束分成两个类别:应用程序(application)和敏感性(sensitivity)。
应用程序约束

应用程序约束定义了可以被具有部署角色的用户访问的资源和属性集合。在默认情况下,唯一被启用的应用程序约束是 core,它包含部署、部署重叠。应用程序约束也包含在 datasources、logging、mail、messaging、naming、resource-adapters 和 security 里(默认不会启用)。这些约束允许 Deployer 用户不仅可以部署应用程序,还可以配置和维护应用程序要求的资源。

应用程序约束的配置位于管理 API 里的 /core-service=management/access=authorization/constraint=application-classification
敏感性约束

敏感性约束定义了被认为是 “敏感的”的资源的集合。敏感的资源通常是某种机密的内容,如密码,或者对服务器的操作有着重大影响的东西,如网络、JVM 配置或系统属性。访问控制系统本身也被视作敏感的资源。

唯一对敏感资源具有写权限的角色是 Administrator 和 SuperUser。Auditor 角色只能读取敏感资源。其他角色都没有访问权限。

应用程序约束的配置位于管理 API 里的 /core-service=management/access=authorization/constraint=sensitivity-classification
库表达式约束

库表达式(Vault Expression)约束定义了读/写库表达式是否被视作敏感操作。在默认情况下,这两者都是敏感操作。

库表达式约束的配置位于管理 API 的 /core-service=management/access=authorization/constraint=vault-expression

目前你不能在管理控制台配置约束。

11.7. 关于 JMX 和基于角色的访问控制

基于角色的访问控制以三种方式应用于 JMX:
  1. JBoss EAP 6 的 Management API 开放为 JMX Management Bean。这些 Management Bean 被称为 "core mbeans",对它们的访问和过滤同底层的 Management API 是一模一样的。
  2. JMX 子系统的写权限被配置为“敏感的”。这表示只有 Administrator 和 SuperUser 角色可以修改这个子系统。而 Auditor 角色可以读取子系统的配置。
  3. 在默认情况下,部署的应用程序和服务(non-core mbeans)注册的 Management Bean 可以被管理用户访问,但只有 Maintainer、Operator、Administrator、SuperUser 角色可以写入它。

11.8. 配置基于角色的访问控制

11.8.1. RBAC 配置任务概述

启用了 RBAC 后,只有具有 Administration 或 SuperUser 角色的用户可以查看和修改访问控制系统。
管理控制台提供了下列常见 RBAC 任务的界面:
  • 查看和配置用户分配(或排斥)了哪些角色
  • 查看和配置组分配(或排斥)了哪些角色
  • 查看每个角色的组和用户成员资格。
  • 配置每个角色默认成员资格。
  • 常见具有作用域的角色
CLI 提供了对整个访问控制系统的访问。这表示在管理控制台里可以完成的任何事情都可以在这里完成,而且你可以用 CLI 执行一些访问控制系统无法完成的任务。
在 CLI 里也可以执行下列任务:
  • 启用和禁用 RBAC
  • 修改权限组合策略
  • 配置应用程序资源和资源敏感性约束

11.8.2. 启用基于角色的访问控制

在默认情况下,RBAC 是被禁用的。将提供者属性从 simple 修改为 rbac 就可以启用它。你可以用 jboss-cli.sh 工具来完成,或者当服务器下线时编辑服务器配置 XML 文件。如果在运行的服务器上禁用或启用 RBAC,在生效前必须重载服务器配置。
启用后,它只能由具有 Administrator 或 SuperUser 角色的用户禁用。在默认情况下,如果jboss-cli.sh 运行在和服务器相同的主机上,它是以 SuperUser 角色运行的。

过程 11.1. 启用 RBAC

  • 要用 jboss-cli.sh 启用 RBAC,请使用访问授权资源的 write-attribute 操作来设置提供者的属性为 rbac
    /core-service=management/access=authorization:write-attribute(name=provider, value=rbac)
    [standalone@localhost:9999 /] /core-service=management/access=authorization:write-attribute(name=provider, value=rbac)
    {
        "outcome" => "success",
        "response-headers" => {
            "operation-requires-reload" => true,
            "process-state" => "reload-required"
        }
    }
    [standalone@localhost:9999 /] /:reload
    {
        "outcome" => "success",
        "result" => undefined
    }
    [standalone@localhost:9999 /]
    

过程 11.2. 禁用 RBAC

  • 要用 jboss-cli.sh 禁用 RBAC,请使用访问授权资源的 write-attribute 操作来设置提供者的属性为 simple
    /core-service=management/access=authorization:write-attribute(name=provider, value=simple)
    [standalone@localhost:9999 /] /core-service=management/access=authorization:write-attribute(name=provider, value=simple)
    {
        "outcome" => "success",
        "response-headers" => {
            "operation-requires-reload" => true,
            "process-state" => "reload-required"
        }
    }
    [standalone@localhost:9999 /] /:reload
    {
        "outcome" => "success",
        "result" => undefined
    }
    [standalone@localhost:9999 /]
    
如果服务器已下线,你可以编辑 XML 配置文件来启用或禁用 RBAC。你可以找到 management 元素下的 access-control 元素里的 provider 属性,设置为 rbac 则启用, simple 则禁用 RBAC。
<management>

        <access-control provider="rbac">
            <role-mapping>
                <role name="SuperUser">
                    <include>
                        <user name="$local"/>
                    </include>
                </role>
            </role-mapping>
        </access-control>

    </management>

11.8.3. 修改权限组合策略

权限组合策略(Permission Combination Policy)定义了用户被分配了多个角色时如何确定权限。它可以设置为 permissiverejecting。默认选项是 permissive
当设置为 permissive 时,如果任何角色被分配给用户,那这个角色许可的动作将会被允许执行。
而当设置为 rejecting 时,如果许可某个动作的多个角色被分配给用户,那这个动作将会被允许执行。
当策略被设置为 rejecting 时,每个用户应该只分配一个角色。当策略为 rejecting 时,具有多个角色的用户将不能使用管理控制台或 jboss-cli.sh
权限组合策略是通过设置 permission-combination-policy 属性为 permissiverejecting 来配置的。这可以用 jboss-cli.sh 工具或编辑服务器配置 XML 文件(如果服务器已下线)来完成。

过程 11.3. 设置权限组合策略

  • 使用访问授权资源的 write-attribute 操作来设置 permission-combination-policy 为所需的策略名称。
    /core-service=management/access=authorization:write-attribute(name=permission-combination-policy, value=POLICYNAME)
    有效的策略名称是 rejecting 和 permissive。
    [standalone@localhost:9999 /] /core-service=management/access=authorization:write-attribute(name=permission-combination-policy, value=rejecting)
    {"outcome" => "success"}
    [standalone@localhost:9999 access=authorization] 
    
    
如果服务器已下线,你可以编辑 XML 配置文件来修改权限组合策略。为此,你需要编辑 access-control 元素的 permission-combination-policy 属性。
<access-control provider="rbac" permission-combination-policy="rejecting">
  <role-mapping>
    <role name="SuperUser">
      <include>
        <user name="$local"/>
      </include>
    </role>
  </role-mapping>
</access-control>

11.9. 管理角色

11.9.1. 关于角色成员资格

当启用了基于角色的控制(RBAC)时,管理用户被允许做的事情是由所分配的角色来决定的。JBoss EAP 6.2 使用了基于用户和组成员资格的包含(include)和排除(exclude)列表来确定用户具有哪些角色。

用户会被认为分配了角色,如果:
  1. 用户被:
    • 列在角色包含列表里,或者
    • 是列在角色包含列表里的组的成员。
  2. 用户没有被:
    • 列在角色排除列表里,或者
    • 是列在角色排除列表里的组的成员。

排除列表优先级高于包含列表。

用户和组的角色包含和排除也可以使用管理控制台和 jboss-cli.sh 工具来完成。

只有具有 SuperUser 或 Administrator 角色的用户才能执行这个配置。

11.9.2. 配置用户和角色的分配

包含或排除用户的角色可以通过管理控制台和 jboss-cli.sh 进行配置。本节只展示如何使用管理控制台来完成。
只有具有 SuperUser 或 Administrator 角色的用户才能执行这些配置。

在管理控制台里可通过下列步骤来配置用户和角色:
  1. 登陆到管理控制台。
  2. 点击『Administration』标签页。
  3. 展开左侧的『Access Control』并选择『Role Assignment』。
  4. 选择『USERS』标签页。
管理控制台里的用户角色管理的截屏

图 11.1. 管理控制台里的用户角色管理

过程 11.4. 为用户创建新的角色分配

  1. 登陆到管理控制台。
  2. 进入『Role Assignment』的『Users』标签页。
  3. 点击用户列表右上角的『Add』按钮。『Add User』对话框会出现。
    『Add User』对话框的截屏

    图 11.2. 『Add User』对话框

  4. 指定用户名,可选择输入区名。
  5. 选择是包含(include)还是排除(exclude)。
  6. 点击角色旁的复选框。你可以使用 Ctl 键(OSX 上的 Command 键)来选择多个选项。
  7. 点击『Save』按钮。
    保存成功后,『Add User』对话框将会关闭,用户列表将会更新以反映所作的修改。如果不成功则会显示 "Failed to save role assignment" 消息。

过程 11.5. 更新用户的角色分配

  1. 登陆到管理控制台。
  2. 进入『Role Assignment』的『Users』标签页。
  3. 从列表里选择用户。
  4. 点击『Edit』。『Selection』面板将进入编辑模式。
    『Selection』编辑视图的截屏

    图 11.3. 『Selection』面板的编辑视图

    在这里你可以添加和删除所分配或排除的角色。
    1. 要添加被分配的角色,从左侧的『Available roles』列表里进行选择并点击『Assigned roles』列表旁的右箭头。角色将从可用列表移至『Assigned roles』列表。
    2. 要删除被分配的角色,从右侧的『Assigned roles』列表里选择要删除的角色,并点击旁边的左箭头。角色将从『Assigned roles』列表移至『Available roles』列表。
    3. 要添加被排除的角色,从左侧的『Available roles』列表里进行选择并点击『Excluded roles』列表旁的右箭头。角色将从可用列表移至『Excluded roles』列表。
    4. 要删除被排除的角色,从右侧的『Excluded roles』列表里选择要删除的角色,并点击旁边的左箭头。角色将从『Excluded roles』列表移至『Available roles』列表。
  5. 点击『Save』按钮。
    成功后,编辑视图将会关闭,用户列表将会更新以反映所作的修改。如果不成功则会显示 "Failed to save role assignment" 消息。

过程 11.6. 删除用户的角色分配

  1. 登陆到管理控制台。
  2. 进入『Role Assignment』的『Users』标签页。
  3. 从列表里选择用户。
  4. 点『Remove』按钮。『Remove Role Assignment』确认框将出现。
  5. 点击『Confirm』按钮。
    成功后,用户将不会再出现在用户角色分配的列表里。

重要

从角色分配列表里删除用户并不会从系统删除这个用户,也不能保证没有角色被分配给这个用户。角色仍可能通过组成员资格分配给用户。

11.9.3. 用 jboss-cli.sh 配置用户角色分配

包含或排除用户的角色可以通过管理控制台和 jboss-cli.sh 进行配置。本节只展示如何使用 jboss-cli.sh 工具来完成。
映射用户/组到角色的配置位于 management API 里的 /core-service=management/access=authorization as role-mapping 元素。
只有具有 SuperUser 或 Administrator 角色的用户才能执行这些配置。

过程 11.7. 查看角色分配配置

  1. 使用 :read-children-names 操作来获取配置角色的完整列表:
    /core-service=management/access=authorization:read-children-names(child-type=role-mapping)
    [standalone@localhost:9999 access=authorization] :read-children-names(child-type=role-mapping)
    {
        "outcome" => "success",
        "result" => [
            "ADMINISTRATOR",
            "DEPLOYER",
            "MAINTAINER",
            "MONITOR",
            "OPERATOR",
            "SuperUser"
        ]
    }
    
  2. 使用指定 role-mapping 的 read-resource 操作来获取某个角色的完整细节:
    /core-service=management/access=authorization/role-mapping=ROLENAME:read-resource(recursive=true)
    [standalone@localhost:9999 access=authorization] ./role-mapping=ADMINISTRATOR:read-resource(recursive=true)
    {
        "outcome" => "success",
        "result" => {
            "include-all" => false,
            "exclude" => undefined,
            "include" => {
                "user-theboss" => {
                    "name" => "theboss",
                    "realm" => undefined,
                    "type" => "USER"
                },
                "user-harold" => {
                    "name" => "harold",
                    "realm" => undefined,
                    "type" => "USER"
                },
                "group-SysOps" => {
                    "name" => "SysOps",
                    "realm" => undefined,
                    "type" => "GROUP"
                }
            }
        }
    }
    [standalone@localhost:9999 access=authorization]
    

过程 11.8. 添加新的角色

这个过程展示了如何添加角色的 role-mapping 条目。这必须在角色可以被配置前完成。
  • 使用 add 操作来添加新的角色配置。
    /core-service=management/access=authorization/role-mapping=ROLENAME:add
    ROLENAME 是新映射使用的角色的名称。
    [standalone@localhost:9999 access=authorization] ./role-mapping=AUDITOR:add             
    {"outcome" => "success"}
    [standalone@localhost:9999 access=authorization]
    

过程 11.9. 添加包含在角色里的用户

这个过程展示了如何添加用户到角色的包含列表里。
如果角色的配置还未完成,那你必须先设置 role-mapping 条目。
  • 请使用 add 操作来添加用户到角色的包含列表里。
    /core-service=management/access=authorization/role-mapping=ROLENAME/include=ALIAS:add(name=USERNAME, type=USER)
    ROLENAME 是被配置的角色的名称。
    ALIAS 是这个映射的唯一名称。红帽推荐你对别名使用命名规则,如 user-USERNAME
    USERNAME 是添加到包含列表里的用户的名称。
     [standalone@localhost:9999 access=authorization] ./role-mapping=AUDITOR/include=user-max:add(name=max, type=USER)
    {"outcome" => "success"}
    [standalone@localhost:9999 access=authorization]
    

过程 11.10. 添加角色所排除的用户

这个过程展示了如何添加用户到角色的排除列表里。
如果角色的配置还未完成,那你必须先设置 role-mapping 条目。
  • 请使用 add 操作来添加用户到角色的排除列表里。
    /core-service=management/access=authorization/role-mapping=ROLENAME/exclude=ALIAS:add(name=USERNAME, type=USER)
    ROLENAME 是被配置的角色的名称。
    USERNAME 是添加到排除列表里的用户的名称。
    ALIAS 是这个映射的唯一名称。红帽推荐你对别名使用命名规则,如 user-USERNAME
    [standalone@localhost:9999 access=authorization] ./role-mapping=AUDITOR/exclude=user-max:add(name=max, type=USER)
    {"outcome" => "success"}
    [standalone@localhost:9999 access=authorization]
    

过程 11.11. 删除用户的角色包含配置

这个过程展示了如何从角色映射里删除用户包含条目。
  • 请使用 remove 操作来删除这个条目。
    /core-service=management/access=authorization/role-mapping=ROLENAME/include=ALIAS:remove
    ROLENAME 是被配置的角色的名称。
    ALIAS 是这个映射的唯一名称。红帽推荐你对别名使用命名规则,如 user-USERNAME
    [standalone@localhost:9999 access=authorization] ./role-mapping=AUDITOR/include=user-max:remove
    {"outcome" => "success"}
    [standalone@localhost:9999 access=authorization]
    
    从包含列表里删除用户并不会从系统删除这个用户,也不能保证角色不会被分配给这个用户。这个角色仍可能根据组成员资格分配给它。

过程 11.12. 删除用户的角色排除配置

这个过程展示了如何从角色映射里删除用户排除条目。
  • 请使用 remove 操作来删除这个条目。
    /core-service=management/access=authorization/role-mapping=ROLENAME/exclude=ALIAS:remove
    ROLENAME 是被配置的角色的名称。
    ALIAS 是这个映射的唯一名称。红帽推荐你对别名使用命名规则,如 user-USERNAME
    [standalone@localhost:9999 access=authorization] ./role-mapping=AUDITOR/exclude=user-max:remove
    {"outcome" => "success"}
    [standalone@localhost:9999 access=authorization]
    
    从排除列表里删除用户并不会从系统删除这个用户,也不能保证角色被分配给这个用户。这个角色仍可能根据组成员资格被排除。

11.9.4. 关于角色和用户组

使用 mgmt-users.properties 文件或 LDAP 服务器验证的用户可以是用户组的成员。用户组是可以分配给一个或多个用户的任意标签。
你可以配置 RBAC 系统根据用户所在的用户组自动分配角色给用户。它也可以根据用户组成员资格来排除用户。
当使用 mgmt-users.properties 文件时,组信息保存在 mgmt-groups.properties 文件里。当使用 LDAP 时,组信息保存在 LDAP 服务器里并由负责 LDAP 服务器的人员来维护。

11.9.5. 配置组角色的分配

角色可以根据用户的用户组成员资格分配给用户。
角色包含或排除组可以通过管理控制台和 jboss-cli.sh 进行配置。本节只展示如何使用管理控制台来完成。
只有具有 SuperUser 或 Administrator 角色的用户才能执行这些配置。
在管理控制台里可通过下列步骤来配置组和角色:
  1. 登陆到管理控制台。
  2. 点击『Administration』标签页。
  3. 展开左侧的『Access Control』并选择『Role Assignment』。
  4. 选择『GROUPS』标签页。
管理控制台里的组角色管理的截屏

图 11.4. 管理控制台里的组角色管理

过程 11.13. 为组创建新的角色分配

  1. 登陆到管理控制台
  2. 进入『Role Assignment』的『GROUPS』标签页。
  3. 点击用户列表右上角的『Add』按钮。『Add Group』对话框会出现。
    『Add Group』对话框的截屏

    图 11.5. 『Add Group』对话框

  4. 指定组名,可选择输入区名。
  5. 选择是包含(include)还是排除(exclude)。
  6. 点击角色旁的复选框。你可以使用 Ctl 键(OSX 上的 Command 键)来选择多个选项。
  7. 点击『Save』按钮。
    保存成功后,『Add Group』对话框将会关闭,组列表将会更新以反映所作的修改。如果不成功则会显示 "Failed to save role assignment" 消息。

过程 11.14. 更新组的角色分配

  1. 登陆到管理控制台。
  2. 进入『Role Assignment』的『GROUPS』标签页。
  3. 从列表里选择组。
  4. 点击『Edit』。『Selection』面板将进入编辑模式。
    编辑模式的『Selection』视图的截屏

    图 11.6. 『Selection』视图编辑模式

    在这里你可以添加和删除组里分配或排除的角色:
    • 要添加被分配的角色,从左侧的『Available roles』列表里进行选择并点击『Assigned roles』列表旁的右箭头。角色将从可用列表移至『Assigned roles』列表。
    • 要删除被分配的角色,从右侧的『Assigned roles』列表里选择要删除的角色,并点击旁边的左箭头。角色将从『Assigned roles』列表移至『Available roles』列表。
    • 要添加被排除的角色,从左侧的『Available roles』列表里进行选择并点击『Excluded roles』列表旁的右箭头。角色将从可用列表移至『Excluded roles』列表。
    • 要删除被排除的角色,从右侧的『Excluded roles』列表里选择要删除的角色,并点击旁边的左箭头。角色将从『Excluded roles』列表移至『Available roles』列表。
  5. 点击『Save』按钮。
    成功后,编辑视图将会关闭,组列表将会更新以反映所作的修改。如果不成功则会显示 "Failed to save role assignment" 消息。

过程 11.15. 删除组的角色分配

  1. 登陆到管理控制台。
  2. 进入『Role Assignment』的『GROUPS』标签页。
  3. 从列表里选择组。
  4. 点『Remove』按钮。『Remove Role Assignment』确认框将出现。
  5. 点击『Confirm』按钮。
    成功后,组将不会再出现在用户角色分配的列表里。
    从角色分配列表里删除组并不会从系统删除这个用户组,也不能保证没有角色被分配给这个组。每个组成员仍可能被直接分配角色。

11.9.6. 用 jboss-cli.sh 配置组角色

角色包含或排除组可以通过管理控制台和 jboss-cli.sh 进行配置。本节只展示如何使用 jboss-cli.sh 工具来完成。
映射用户/组到角色的配置位于 management API 里的 /core-service=management/access=authorization as role-mapping 元素。
只有具有 SuperUser 或 Administrator 角色的用户才能执行这些配置。

过程 11.16. 查看组角色分配配置

  1. 使用 read-children-names 操作来获取配置角色的完整列表:
    /core-service=management/access=authorization:read-children-names(child-type=role-mapping)
    [standalone@localhost:9999 access=authorization] :read-children-names(child-type=role-mapping)
    {
        "outcome" => "success",
        "result" => [
            "ADMINISTRATOR",
            "DEPLOYER",
            "MAINTAINER",
            "MONITOR",
            "OPERATOR",
            "SuperUser"
        ]
    }
    
  2. 使用指定 role-mapping 的 read-resource 操作来获取某个角色的完整细节:
    /core-service=management/access=authorization/role-mapping=ROLENAME:read-resource(recursive=true)
    [standalone@localhost:9999 access=authorization] ./role-mapping=ADMINISTRATOR:read-resource(recursive=true)
    {
        "outcome" => "success",
        "result" => {
            "include-all" => false,
            "exclude" => undefined,
            "include" => {
                "user-theboss" => {
                    "name" => "theboss",
                    "realm" => undefined,
                    "type" => "USER"
                },
                "user-harold" => {
                    "name" => "harold",
                    "realm" => undefined,
                    "type" => "USER"
                },
                "group-SysOps" => {
                    "name" => "SysOps",
                    "realm" => undefined,
                    "type" => "GROUP"
                }
            }
        }
    }
    [standalone@localhost:9999 access=authorization]
    

过程 11.17. 添加新的角色

这个过程展示了如何添加角色的 role-mapping 条目。这必须在角色可以被配置前完成。
  • 使用 add 操作来添加新的角色配置。
    /core-service=management/access=authorization/role-mapping=ROLENAME:add
    [standalone@localhost:9999 access=authorization] ./role-mapping=AUDITOR:add             
    {"outcome" => "success"}
    [standalone@localhost:9999 access=authorization]
    

过程 11.18. 添加包含在角色里的组

这个过程展示了如何添加组到角色的包含列表里。
如果角色的配置还未完成,那你必须先设置 role-mapping 条目。
  • 请使用 add 操作来添加组到角色的包含列表里。
    /core-service=management/access=authorization/role-mapping=ROLENAME/include=ALIAS:add(name=GROUPNAME, type=GROUP)
    ROLENAME 是被配置的角色的名称。
    GROUPNAME 是添加到包含列表里的组的名称。
    ALIAS 是这个映射的唯一名称。红帽推荐你对别名使用命名规则,如 group-GROUPNAME
    [standalone@localhost:9999 access=authorization] ./role-mapping=AUDITOR/include=group-investigators:add(name=investigators, type=GROUP)
    {"outcome" => "success"}
    [standalone@localhost:9999 access=authorization]
    

过程 11.19. 添加角色所排除的组

这个过程展示了如何添加组到角色的排除列表里。
如果角色的配置还未完成,那你必须先创建 role-mapping 条目。
  • 请使用 add 操作来添加组到角色的排除列表里。
    /core-service=management/access=authorization/role-mapping=ROLENAME/exclude=ALIAS:add(name=GROUPNAME, type=GROUP)
    ROLENAME 是被配置的角色的名称。
    GROUPNAME 是添加到包含列表里的组的名称。
    ALIAS 是这个映射的唯一名称。红帽推荐你对别名使用命名规则,如 group-GROUPNAME
    [standalone@localhost:9999 access=authorization] ./role-mapping=AUDITOR/exclude=group-supervisors:add(name=supervisors, type=USER)
    {"outcome" => "success"}
    [standalone@localhost:9999 access=authorization]
    

过程 11.20. 删除组的角色包含配置

这个过程展示了如何从角色映射里删除组包含条目。
  • 请使用 remove 操作来删除这个条目。
    /core-service=management/access=authorization/role-mapping=ROLENAME/include=ALIAS:remove
    ROLENAME 是被配置的角色的名称。
    ALIAS 是这个映射的唯一名称。红帽推荐你对别名使用命名规则,如 group-GROUPNAME
    [standalone@localhost:9999 access=authorization] ./role-mapping=AUDITOR/include=group-investigators:remove
    {"outcome" => "success"}
    [standalone@localhost:9999 access=authorization]
    
    从包含列表里删除组并不会从系统删除这个组,也不能保证角色不会被分配给这个组。这个角色仍可能分配给组里的用户。

过程 11.21. 删除组的角色排除配置

这个过程展示了如何从角色映射里删除组排除条目。
  • 请使用 remove 操作来删除这个条目。
    /core-service=management/access=authorization/role-mapping=ROLENAME/exclude=ALIAS:remove
    ROLENAME 是被配置的角色的名称。
    ALIAS 是这个映射的唯一名称。红帽推荐你对别名使用命名规则,如 group-GROUPNAME
    [standalone@localhost:9999 access=authorization] ./role-mapping=AUDITOR/exclude=group-supervisors:remove
    {"outcome" => "success"}
    [standalone@localhost:9999 access=authorization]
    
    从排除列表里删除组并不会从系统删除这个组,也不能保证角色被分配给这个组。这个角色仍可能根据组成员资格被排除。

11.9.7. 关于用 LDAP 进行授权和组加载

在 LDAP 目录里,有些条目用于用户帐号而有些条目用于组,并通过使用属性来进行交叉引用。有些属性是从用户帐号引用组条目,或者是组上的属性引用作为组成员的用户。在一些服务器上,这两种交叉引用的形式都存在。

用户使用简单的用户名通过服务器来验证也是很常见的,搜索组成员信息取决于使用的目录服务器。你可以使用简单名称来执行搜索,也可以用目录里的用户条目的标识名来执行。

用户连接服务器的验证步骤总是先发生的,一旦它已经决定用户成功验证,服务器将开始加载用户组。因为验证和授权步骤都使用到 LDAP 服务器的连接,安全区包含一个优化办法,也就是任何用于验证的连接都将被组加载步骤重用。如下面的配置步骤所展示的,你可以在授权部分定义角色来将用户的简单名称转换为标识名,这可能复制在验证步骤发生的搜索,所以,如果用户名到标识名的搜索已被执行,那么这个搜索的结果就可以被缓存和重用而无需再次进行搜索。
<authorization>
    <ldap connection="...">
       <username-to-dn> <!-- OPTIONAL -->
           <!-- Only one of the following. -->
           <username-is-dn />
           <username-filter base-dn="..." recursive="..." user-dn-attribute="..." attribute="..." />
           <advanced-filter base-dn="..." recursive="..." user-dn-attribute="..." filter="..." />
        </username-to-dn>
       <group-search group-name="..." iterative="..." group-dn-attribute="..." group-name-attribute="..." >
           <!-- One of the following -->
           <group-to-principal base-dn="..." recursive="..." search-by="...">
               <membership-filter principal-attribute="..." />
           </group-to-principal>
           <principal-to-group group-attribute="..." />
       </group-search>
    </ldap>
</authorization>

重要

有些例子里的属性是用默认值设置的,在此出现的目的是为了说明而已。当服务器进行持久化时,这些包含默认值的属性将从配置里删除。

username-to-dn

如上所述,有时候你需要在授权配置里定义如何从被验证的用户的用户名映射到 LDAP 目录里条目的标识名。username-to-dn 元素是关于它的定义的,只有下列两者都为 true 才要求定义这个元素:
  • 验证步骤不是通过 LDAP 进行的。
  • 组搜索在搜索过程中使用标识名。

请注意,当你阅读下面的例子时,你会觉得其验证配置是重复的,确实是这样 - 如果你只使用 LDAP 进行验证,那这并非必需的,因为将在验证过程中获取标识名。
1:1 username-to-dn

这个是最基本的配置形式,用于指定远程用户输入的用户名实际上是标识名。
<username-to-dn>
   <username-is-dn />
</username-to-dn>

因为这是定义的 1:1 的映射,所以没有其他可能的配置。
username-filter

下一个选项和上面验证步骤描述的选项非常类似,它简单地按照提供的用户名进行搜索。
<username-to-dn>
    <username-filter base-dn="dc=people,dc=harold,dc=example,dc=com" recursive="false" attribute="sn" user-dn-attribute="dn" />
</username-to-dn>

可以在这里设置的属性是:
  • base-dn: 开始搜索的上下文的标识名。
  • recursive:搜索是否将扩展到子上下文。默认值为 false
  • attribute:根据提供的用户名尝试和匹配的用户条目的属性。默认值为 uid
  • user-dn-attribute:为获得用户标识名而读取的属性。默认值为 dn
advanced-filter

这个最后的选项指定了高级过滤器,和验证部分一样,这是使用自定义过滤器来定位用户标识名的机会。
<username-to-dn>
    <advanced-filter base-dn="dc=people,dc=harold,dc=example,dc=com" recursive="false" filter="sAMAccountName={0}" user-dn-attribute="dn" />
</username-to-dn>

对应 username-filter 的属性的默认值都是一样的,这里不再列出。新的属性是:
  • filter:用于搜索用户条目的过滤器,用户名将在占位符 {0} 里替换。

重要

XML 格式必须保持有效,定义过滤器来确保特殊字符(如 &)的正确格式。例如,对于 & 字符是 &amp;

组搜索

如上面所描述的,在搜索组成员资格信息时你可以使用两种风格。第一种是用户条目包含引用用户所属组的属性。第二种风格是组包含引用用户条目的属性。

当可以选择风格时,红帽推荐引用所用组的用户条目的配置。这是因为用这个方法时组信息可以通过读取已知标识名来加载而无需执行任何搜索。另外一个方法则要求额外的搜索来确定引用用户的组。

在描述配置之前,这里有几个 LDIF 例子来进行解释。

例 11.1. Principal to Group - LDIF 示例。

这个例子展示了用户 TestUserOne,它是 GroupOne 的成员,而 GroupOne 也是 GroupFive 的成员。通过属性 memberOf 展示了组成员资格,它被设置为用户(或组)所属的组的标识名。

这里没有展示的是用户可能具有多个 memberOf 的属性集,每个都对应该用户所属的组。
dn: uid=TestUserOne,ou=users,dc=principal-to-group,dc=example,dc=org
objectClass: extensibleObject
objectClass: top
objectClass: groupMember
objectClass: inetOrgPerson
objectClass: uidObject
objectClass: person
objectClass: organizationalPerson
cn: Test User One
sn: Test User One
uid: TestUserOne
distinguishedName: uid=TestUserOne,ou=users,dc=principal-to-group,dc=example,dc=org
memberOf: uid=GroupOne,ou=groups,dc=principal-to-group,dc=example,dc=org
memberOf: uid=Slashy/Group,ou=groups,dc=principal-to-group,dc=example,dc=org
userPassword:: e1NTSEF9WFpURzhLVjc4WVZBQUJNbEI3Ym96UVAva0RTNlFNWUpLOTdTMUE9PQ==

dn: uid=GroupOne,ou=groups,dc=principal-to-group,dc=example,dc=org
objectClass: extensibleObject
objectClass: top
objectClass: groupMember
objectClass: group
objectClass: uidObject
uid: GroupOne
distinguishedName: uid=GroupOne,ou=groups,dc=principal-to-group,dc=example,dc=org
memberOf: uid=GroupFive,ou=subgroups,ou=groups,dc=principal-to-group,dc=example,dc=org

dn: uid=GroupFive,ou=subgroups,ou=groups,dc=principal-to-group,dc=example,dc=org
objectClass: extensibleObject
objectClass: top
objectClass: groupMember
objectClass: group
objectClass: uidObject
uid: GroupFive
distinguishedName: uid=GroupFive,ou=subgroups,ou=groups,dc=principal-to-group,dc=example,dc=org

例 11.2. Group to Principal - LDIF 示例

这个例子展示了相同的用户 TestUserOne,它是 GroupOne 的成员(反之也是 GroupFive 的成员)- 然而在这个例子里,它是一个用于交叉引用的组到用户的属性 uniqueMember

用于组成员资格交叉引用的属性可以重复,如果你查看 GroupFive,那里也有一个没有显示在这里的对其他用户 TestUserFive 的引用。
dn: uid=TestUserOne,ou=users,dc=group-to-principal,dc=example,dc=org
objectClass: top
objectClass: inetOrgPerson
objectClass: uidObject
objectClass: person
objectClass: organizationalPerson
cn: Test User One
sn: Test User One
uid: TestUserOne
userPassword:: e1NTSEF9SjR0OTRDR1ltaHc1VVZQOEJvbXhUYjl1dkFVd1lQTmRLSEdzaWc9PQ==

dn: uid=GroupOne,ou=groups,dc=group-to-principal,dc=example,dc=org
objectClass: top
objectClass: groupOfUniqueNames
objectClass: uidObject
cn: Group One
uid: GroupOne
uniqueMember: uid=TestUserOne,ou=users,dc=group-to-principal,dc=example,dc=org

dn: uid=GroupFive,ou=subgroups,ou=groups,dc=group-to-principal,dc=example,dc=org
objectClass: top
objectClass: groupOfUniqueNames
objectClass: uidObject
cn: Group Five
uid: GroupFive
uniqueMember: uid=TestUserFive,ou=users,dc=group-to-principal,dc=example,dc=org
uniqueMember: uid=GroupOne,ou=groups,dc=group-to-principal,dc=example,dc=org

通用组搜索

通过上面例子里展示的两种方法,我们知道首先需要定义两者共用的属性。
<group-search group-name="..." iterative="..." group-dn-attribute="..." group-name-attribute="..." >
    ...
</group-search>
  • group-name:这个属性用来指定用于作为用户所属组列表返回的组名的格式。这可以是组名的简单格式或者是组的标识名,如果标识名是必需的,那这个属性可以设置为 DISTINGUISHED_NAME。默认值是 SIMPLE
  • iterative:这个属性用来指定在确定用户所属的组之后是否应该根据组所属的组迭代地进行搜索。如果启用了迭代搜索,那么我们将继续搜索,直到到达不是成员的组或检测到循环。它的默认值为 false

循环的组成员并不是一个问题。我们保存了每次搜索的记录来防止已搜索过的组被再次搜索。

重要

为了迭代式搜索能正常进行,组条目需要和用户条目一致,也就是使用相同的方法来确定用户所属的组和确定组所属组。但对于组到组的成员关系,如果用于交叉引用的属性或者引用方向有改动,那这就无法实现了。
  • group-dn-attribute:组条目上的哪个属性是其标识名。默认为 dn
  • group-name-attribute:组条目上的哪个属性是其简单名。默认为 uid

例 11.3. Principal to Group 配置示例

基于上面的 LDIF 例子,下面是一个循环加载用户组的配置示例,用来进行交叉引用的属性是用户的 memberOf
<authorization>
    <ldap connection="LocalLdap">
        <username-to-dn>
            <username-filter base-dn="ou=users,dc=principal-to-group,dc=example,dc=org" recursive="false" attribute="uid" user-dn-attribute="dn" />
        </username-to-dn>
        <group-search group-name="SIMPLE" iterative="true" group-dn-attribute="dn" group-name-attribute="uid">
            <principal-to-group group-attribute="memberOf" />
        </group-search>
    </ldap>
</authorization>

这个配置最重要的方面是已添加了带有单个属性的 principal-to-group 元素。
  • group-attribute

例 11.4. Group to Principal 配置示例

这个例子展示了一个对上面的 group to principal LDIF 例子的迭代搜索。
<authorization>
      <ldap connection="LocalLdap">
          <username-to-dn>
              <username-filter base-dn="ou=users,dc=group-to-principal,dc=example,dc=org" recursive="false" attribute="uid" user-dn-attribute="dn" />
          </username-to-dn>
          <group-search group-name="SIMPLE" iterative="true" group-dn-attribute="dn" group-name-attribute="uid">
              <group-to-principal base-dn="ou=groups,dc=group-to-principal,dc=example,dc=org" recursive="true" search-by="DISTINGUISHED_NAME">
                  <membership-filter principal-attribute="uniqueMember" />
              </group-to-principal>
          </group-search>
      </ldap>
  </authorization>

在这里添加了 group-to-principal 元素,这个元素用来定义引用用户条目的组搜索如何只执行,它将设置下列属性:
  • base-dn: 开始搜索的上下文的标识名。
  • recursive:是否搜索子上下文。默认为 false
  • search-by:在搜索里使用的角色名称的格式。有效值是 SIMPLEDISTINGUISHED_NAME。默认值为 DISTINGUISHED_NAME

group-to-principal 元素里有一个定义交叉引用的 membership-filter 元素。
  • principal-attribute:引用用户条目的组条目上的属性名称。默认值为 member

11.9.8. 关于带作用域的角色

带作用域的角色(Scoped Role)是用户定义的角色,它赋予标准角色中一个角色的权限,但只用于一个或多个指定的服务器组或主机。带作用域的角色允许按需要赋予管理用户限于某些服务器组或主机的权限。

带作用域的角色可以由具有 Administrator 或 SuperUser 角色的用户来创建。

它们是由 5 个特征定义的:
  1. 唯一的名字。
  2. 基于那些标准角色。
  3. 是否应用在服务器组或主机上。
  4. 所限的服务器组或主机的列表。
  5. 是否自动包括所有用户。默认为 false。

创建之后,带作用域的角色可以和标准角色一样分配给用户和组。

创建带作用域的角色并不意味着要定义新的权限。带作用域的角色只能用来在有限的作用域李应用现存角色的权限。例如,你可以根据限于单个服务器组的 Deployer 角色创建带作用域的角色。

角色只可以限于两个作用域,主机和服务器组。
作用域为主机的角色

作用域为主机的角色将它的权限限制到一个或多个主机。这意味着为相关的 /host=*/ 资源树提供了访问权限但其他主机所专有的资源是隐藏的。
作用域为服务器组的角色

作用域为服务器组的角色限制了该角色的权限到一个或多个服务器组。此外,角色权限将应用于和指定的服务器组相关联的配置集、套接字绑定组、服务器配置和服务器资源。和这个服务器组逻辑上不关联的资源的任何子资源对于用户来说都是不可见的。

作用域为主机和服务器组的角色都具有受管域配置的其他部分的 Monitor 角色的权限。

11.9.9. 创建带作用域的角色

带作用域的角色(Scoped Role)是用户定义的角色,它赋予标准角色中一个角色的权限,但只用于一个或多个指定的服务器组或主机。本节将展示如何创建带作用域的角色。

只有具有 SuperUser 或 Administrator 角色的用户才能执行这些配置。

在管理控制台里可通过下列步骤来配置带作用域的角色:
  1. 登录到管理控制台
  2. 点击 Administration 标签页
  3. 展开左侧的 Access Control 并选择 Role Assignment
  4. 选择 ROLES 标签页,然后选择里面的 Scoped Roles 标签页。
Scoped Role Configuration in the Management Console

图 11.7. 在管理控制台里配置带作用域的角色

管理控制台的 Scoped Roles fen 部分由两个主要区域组成,包含当前配置的带作用域的角色的表,以及显示表里当前别选择的角色细节。
下面的过程展示了如何配置带作用域的角色。

过程 11.22. 添加新的带作用域的角色

  1. 登录到管理控制台
  2. 导航至 Roles 标签页的 Scoped Roles 区域。
  3. 点击 Add 按钮。Add Scoped Role 对话框将会出现。
    Add Scoped Role Dialog

    图 11.8. Add Scoped Role 对话框

  4. 指定下列细节:
    • Name,新的带作用域的角色的唯一名称。
    • Base Role,这个角色的权限所基于的角色。
    • Type,这个角色是否将被限于主机或服务器组。
    • Scope,这个角色所限于的主机或服务器组列表。可以选择多重条目。
    • Include All,这个角色是否自动包含所有的用户。默认为 no。
  5. 点击 Save 按钮,对话框将关闭且新创建的角色将出现在表里。

过程 11.23. 编辑带作用域的角色

  1. 登录到管理控制台
  2. 导航至 Roles 标签页的 Scoped Roles 区域。
  3. 点击你要编辑的带作用域的角色。这个角色的细节将出现在表下面的 Selection 面板上。
    Role Selected

    图 11.9. 选择的角色

  4. 点击 Selection 面板上的 Edit 链接。Selection 面板将进入编辑模式。
    Selection Panel in Edit Mode

    图 11.10. 处于编辑模式的 Selection 面板

  5. 更新你要修改的细节并点击 Save 按钮。Selection 面板将回到之前的状态。Selection 面板和表都会显示最近更新的细节。

过程 11.24. 查看 Scoped Role 成员

  1. 登录到管理控制台
  2. 导航至 Roles 标签页的 Scoped Roles 区域。
  3. 点击表里你要查看成员的带作用域的角色,然后点击 Members 按钮。Members of role 对话框将出现。它将显示这个角色包含或排斥的用户和组。
    Role Membership Dialog

    图 11.11. Role Membership 对话框

  4. 当你已经完成查看这些信息后请点击 Done 按钮。

过程 11.25. 删除带作用域的角色

重要

如果用户或组被分配至某个角色,这个角色就不能被删除。你需要先删除角色分配。
  1. 登录到管理控制台
  2. 导航至 Roles 标签页的 Scoped Roles 区域。
  3. 选择表里要删除的带作用域的角色。
  4. 点击 Remove 按钮。Remove Scoped Role 对话框将出现。
  5. 点击 Confirm 按钮。对话框将关闭且角色会被删除。

11.10. 配置约束

11.10.1. 配置 Sensitivity 约束

每个敏感性约束(Sensitivity Constraint)都定义了被认为是 “敏感的”的资源的集合。敏感的资源通常是某种机密的内容,如密码,或者对服务器的操作有着重大影响的东西,如网络、JVM 配置或系统属性。访问控制系统本身也被视作敏感的资源。资源敏感性限制了哪些角色可以读、写或寻址专门的资源。

应用程序约束的配置位于管理 API 里的 /core-service=management/access=authorization/constraint=sensitivity-classification

在管理模型内部,每个 Sensitivity 约束都被标识为类别(classification)。这些类别被分组为类型(types)。有 39 种类别被分组成 13 个类型。

要配置 Sensitivity 约束,请使用 write-attribute 操作来设置 configured-requires-readconfigured-requires-writeconfigured-requires-addressable 属性。要使该类型的操作成为敏感的,请将其设置为 false。在默认情况下,它们不会被设置而使用 default-requires-readdefault-requires-writedefault-requires-addressable 的值。一旦配置的属性被设置,它将替代默认值。默认值不能被修改。

例 11.5. 使读取系统属性成为敏感性操作

[domain@localhost:9999 /] cd /core-service=management/access=authorization/constraint=sensitivity-classification/type=core/classification=system-property
[domain@localhost:9999 classification=system-property] :write-attribute(name=configured-requires-read, value=true)
{
    "outcome" => "success",
    "result" => undefined,
    "server-groups" => {"main-server-group" => {"host" => {"master" => {
        "server-one" => {"response" => {"outcome" => "success"}},
        "server-two" => {"response" => {"outcome" => "success"}}
    }}}}
}
[domain@localhost:9999 classification=system-property] :read-resource
{
    "outcome" => "success",
    "result" => {
        "configured-requires-addressable" => undefined,
        "configured-requires-read" => true,
        "configured-requires-write" => undefined,
        "default-requires-addressable" => false,
        "default-requires-read" => false,
        "default-requires-write" => true,
        "applies-to" => {
            "/host=master/system-property=*" => undefined,
            "/host=master/core-service=platform-mbean/type=runtime" => undefined,
            "/server-group=*/system-property=*" => undefined,
            "/host=master/server-config=*/system-property=*" => undefined,
            "/host=master" => undefined,
            "/system-property=*" => undefined,
            "/" => undefined
        }
    }
}
[domain@localhost:9999 classification=system-property]

表 11.2 “Sensitivity 约束配置结果” 总结了哪些角色能够执行哪些操作所取决的配置。

表 11.2. Sensitivity 约束配置结果

requires-read requires-write requires-addressable

true

读操作是敏感的。

只有 Auditor, Administrator 和 SuperUser 可以读。

写操作是敏感的。

只有 Administrator 和 SuperUser 可以写

寻址(Addressing)是敏感的。

只有 Auditor, Administrator 和 SuperUser 可以寻址。

false

读操作是非敏感的。

任何管理用户都可以读。

写操作是非敏感的。

只有 Maintainer, Administrator 和 SuperUser 可以写。如果这个约束是一个应用程序资源的话,Deployers 也可以写。

寻址(Addressing)是非敏感的。

任何管理用户都可以寻址。

11.10.2. 配置应用程序资源约束

每个应用程序资源约束都定义了一系列通常和应用程序及服务部署相关联的资源、属性和操作。当启用了应用程序资源约束时,具有 Deployer 角色的管理用户会被赋予访问资源的权限。

应用程序约束配置位于 /core-service=management/access=authorization/constraint=application-classification/ 里的管理模型。

在管理模型内部,每个应用程序资源约束都被标识为类别(classification)。这些类别被分组为类型(types)。有 14 种类别被分组成 8 个类型。每个类别都有一个 applies-to 元素,它是一个类别配置应用的资源路径模式的列表。

在默认情况下,唯一启用的应用程序资源类别是 core。Core 包含部署、部署重叠和部署操作。

要启用应用程序资源,请使用 write-attribute 操作来设置 configured-application attributetrue。要禁用应用程序资源,请设置这个属性为 false。在默认情况下,这些属性没被设置且使用了 default-application 属性的值。这个默认值不能被修改。

例 11.6. 启用 logger-profile 应用程序资源归类

[domain@localhost:9999 /] cd /core-service=management/access=authorization/constraint=application-classification/type=logging/classification=logging-profile
[domain@localhost:9999 classification=logging-profile] :write-attribute(name=configured-application, value=true)
{
    "outcome" => "success",
    "result" => undefined,
    "server-groups" => {"main-server-group" => {"host" => {"master" => {
        "server-one" => {"response" => {"outcome" => "success"}},
        "server-two" => {"response" => {"outcome" => "success"}}
    }}}}
}
[domain@localhost:9999 classification=logging-profile] :read-resource 
{
    "outcome" => "success",
    "result" => {
        "configured-application" => true,
        "default-application" => false,
        "applies-to" => {"/profile=*/subsystem=logging/logging-profile=*" => undefined}
    }
}
[domain@localhost:9999 classification=logging-profile]

重要

应用程序资源约束应用域所有匹配它的配置的资源。例如,只将 Deployer 用户权限赋予一个数据源资源而不是另外一个是不可能的。如果需要这种级别的分离,那么我们推荐在其他的服务器组里配置资源并为每个组创建不同作用域的 Deployer 角色。

11.10.3. 配置 Vault 表达式约束

在默认情况下,读和写 Vault 表达式是敏感操作。配置 Vault 表达式约束允许你设置这两个操作为非敏感的。修改这个约束允许更多的角色读和写 Vault 表达式。

Vault 表达式约束的配置位于 /core-service=management/access=authorization/constraint=vault-expression 上的管理模型里。

要配置 Vault 表达式,请使用 write-attribute 操作来设置 configured-requires-writeconfigured-requires-read 的属性为 truefalse。在默认情况下,它们不会被设置而使用 default-requires-readdefault-requires-write 的值。默认值不能被修改。

例 11.7. 使写入 Vault 表示成为非敏感操作

[domain@localhost:9999 /] cd /core-service=management/access=authorization/constraint=vault-expression
[domain@localhost:9999 constraint=vault-expression] :write-attribute(name=configured-requires-write, value=false)
{
    "outcome" => "success",
    "result" => undefined,
    "server-groups" => {"main-server-group" => {"host" => {"master" => {
        "server-one" => {"response" => {"outcome" => "success"}},
        "server-two" => {"response" => {"outcome" => "success"}}
    }}}}
}
[domain@localhost:9999 constraint=vault-expression] :read-resource
{
    "outcome" => "success",
    "result" => {
        "configured-requires-read" => undefined,
        "configured-requires-write" => false,
        "default-requires-read" => true,
        "default-requires-write" => true
    }
}
[domain@localhost:9999 constraint=vault-expression]

表 11.3 “Vault 表达式约束配置结果” 总结了哪些角色能够读取和写入 Vault 表达式所取决的配置。

表 11.3. Vault 表达式约束配置结果

requires-read requires-write

true

读操作是敏感的。

只有 Auditor, Administrator 和 SuperUser 可以读。

写操作是敏感的。

只有 Administrator 和 SuperUser 可以写。

false

读操作是不敏感的。

所有管理用户都可以读。

写操作是不敏感的。

Monitor, Administrator 和 SuperUser 都可以写。如果 Vault 表达式是一个应用程序资源的话,Deployers 也可以写。

11.11. 约束引用

11.11.1. 应用程序资源约束引用

类型:core

类别:deployment-overlay
  • 默认值:true
    PATH 属性 操作

    /deployment-overlay=*

    /deployment=*

    /

    upload-deployment-stream, full-replace-deployment, upload-deployment-url, upload-deployment-bytes

类型:datasources

类别:datasource
  • 默认值:false
    PATH 属性 操作

    /deployment=*/subdeployment=*/subsystem=datasources/data-source=*

    /subsystem=datasources/data-source=*

    /subsystem=datasources/data-source=ExampleDS

    /deployment=*/subsystem=datasources/data-source=*
Classification: jdbc-driver
  • 默认值:false
    PATH 属性 操作

    /subsystem=datasources/jdbc-driver=*
类别:xa-data-source
  • 默认值:false
    PATH 属性 操作

    /subsystem=datasources/xa-data-source=*

    /deployment=*/subsystem=datasources/xa-data-source=*

    /deployment=*/subdeployment=*/subsystem=datasources/xa-data-source=*

类型:logging

类别:logger
  • 默认值:false
    PATH 属性 操作

    /subsystem=logging/logger=*

    /subsystem=logging/logging-profile=*/logger=*
类别:logging-profile
  • 默认值:false
    PATH 属性 操作

    /subsystem=logging/logging-profile=*

类型:mail

类别:mail-session
  • 默认值:false
    PATH 属性 操作

    /subsystem=mail/mail-session=*

类型:naming

类别:binding
  • 默认值:false
    PATH 属性 操作

    /subsystem=naming/binding=*

类型:resource-adapters

类别:resource-adapters
  • 默认值:false
    PATH 属性 操作

    /subsystem=resource-adapters/resource-adapter=*

类型:security

类别:security-domain
  • 默认值:false
    PATH 属性 操作

    /subsystem=security/security-domain=*

11.11.2.  安全约束引用

类型:core

类别:access-control
  • requires-addressable: true
  • requires-read: true
  • requires-write: true
    PATH 属性 操作

    /core-service=management/access=authorization

    /subsystem=jmx

    non-core-mbean-sensitivity
类别:credential
  • requires-addressable: false
  • requires-read: true
  • requires-write: true
    PATH 属性 操作

    /subsystem=mail/mail-session=*/server=pop3

    username , password

    /subsystem=mail/mail-session=*/server=imap

    username, password

    /subsystem=datasources/xa-data-source=*

    user-name, recovery-username, password, recovery-password

    /subsystem=mail/mail-session=*/custom=*

    username, password

    /subsystem=datasources/data-source=*"

    user-name, password

    /subsystem=remoting/remote-outbound-connection=*"

    username

    /subsystem=mail/mail-session=*/server=smtp

    username , password

    /subsystem=web/connector=*/configuration=ssl

    key-alias, password

    /subsystem=resource-adapters/resource-adapter=*/connection-definitions=*"

    recovery-username, recovery-password
类别:domain-controller
  • requires-addressable: false
  • requires-read: false
  • requires-write: true
    PATH 属性 操作
类别:domain-names
  • requires-addressable: false
  • requires-read: false
  • requires-write: true
    PATH 属性 操作
类别:extensions
  • requires-addressable: false
  • requires-read: false
  • requires-write: true
    PATH 属性 操作

    /extension=*
类别:jvm
  • requires-addressable: false
  • requires-read: false
  • requires-write: true
    PATH 属性 操作

    /core-service=platform-mbean/type=runtime

    input-arguments, boot-class-path, class-path, boot-class-path-supported, library-path
类别:management-interfaces
  • requires-addressable: false
  • requires-read: false
  • requires-write: true
    PATH 属性 操作

    /core-service=management/management-interface=native-interface

    /core-service=management/management-interface=http-interface
类别:module-loading
  • requires-addressable: false
  • requires-read: false
  • requires-write: true
    PATH 属性 操作

    /core-service=module-loading
类别:patching
  • requires-addressable: false
  • requires-read: false
  • requires-write: true
    PATH 属性 操作

    /core-service=patching/addon=*

    /core-service=patching/layer=*"

    /core-service=patching
类别:read-whole-config
  • requires-addressable: false
  • requires-read: true
  • requires-write: true
    PATH 属性 操作

    /

    read-config-as-xml
类别:security-domain
  • requires-addressable: true
  • requires-read: true
  • requires-write: true
    PATH 属性 操作

    /subsystem=security/security-domain=*
类别:security-domain-ref
  • requires-addressable: true
  • requires-read: true
  • requires-write: true
    PATH 属性 操作

    /subsystem=datasources/xa-data-source=*

    security-domain

    /subsystem=datasources/data-source=*

    security-domain

    /subsystem=ejb3

    default-security-domain

    /subsystem=resource-adapters/resource-adapter=*/connection-definitions=*

    security-domain, recovery-security-domain, security-application, security-domain-and-application
类别:security-realm
  • requires-addressable: true
  • requires-read: true
  • requires-write: true
    PATH 属性 操作

    /core-service=management/security-realm=*
类别:security-realm-ref
  • requires-addressable: true
  • requires-read: true
  • requires-write: true
    PATH 属性 操作

    /subsystem=remoting/connector=*

    security-realm

    /core-service=management/management-interface=native-interface

    security-realm

    /core-service=management/management-interface=http-interface

    security-realm

    /subsystem=remoting/remote-outbound-connection=*

    security-realm
类别:security-vault
  • requires-addressable: false
  • requires-read: false
  • requires-write: true
    PATH 属性 操作

    /core-service=vault
类别:service-container
  • requires-addressable: false
  • requires-read: false
  • requires-write: true
    PATH 属性 操作

    /core-service=service-container
类别:snapshots
  • requires-addressable: false
  • requires-read: false
  • requires-write: false
    PATH 属性 操作

    /

    take-snapshot, list-snapshots, delete-snapshot
类别:socket-binding-ref
  • requires-addressable: false
  • requires-read: false
  • requires-write: false
    PATH 属性 操作

    /subsystem=mail/mail-session=*/server=pop3

    outbound-socket-binding-ref

    /subsystem=mail/mail-session=*/server=imap

    outbound-socket-binding-ref

    /subsystem=remoting/connector=*

    socket-binding

    /subsystem=web/connector=*

    socket-binding

    /subsystem=remoting/local-outbound-connection=*

    outbound-socket-binding-ref

    /socket-binding-group=*/local-destination-outbound-socket-binding=*

    socket-binding-ref

    /subsystem=remoting/remote-outbound-connection=*

    outbound-socket-binding-ref

    /subsystem=mail/mail-session=*/server=smtp

    outbound-socket-binding-ref

    /subsystem=transactions

    process-id-socket-binding, status-socket-binding, socket-binding
类别:socket-config
  • requires-addressable: false
  • requires-read: false
  • requires-write: true
    PATH 属性 操作

    /interface=*

    resolve-internet-address

    /core-service=management/management-interface=native-interface

    port, interface, socket-binding

    /socket-binding-group=*

    /core-service=management/management-interface=http-interface

    port, secure-port, interface, secure-socket-binding, socket-binding

    /

    resolve-internet-address

    /subsystem=transactions

    process-id-socket-max-ports
类别:system-property
  • requires-addressable: false
  • requires-read: false
  • requires-write: true
    PATH 属性 操作

    /core-service=platform-mbean/type=runtime

    system-properties

    /system-property=*

    /

    resolve-expression

类型:datasources

类别:data-source-security
  • requires-addressable: false
  • requires-read: true
  • requires-write: true
    PATH 属性 操作

    /subsystem=datasources/xa-data-source=*

    user-name, security-domain, password

    /subsystem=datasources/data-source=*

    user-name, security-domain, password

类型:jdr

类别:jdr
  • requires-addressable: false
  • requires-read: false
  • requires-write: true
    PATH 属性 操作

    /subsystem=jdr

    generate-jdr-report

类型:jmx

类别:jmx
  • requires-addressable: false
  • requires-read: false
  • requires-write: true
    PATH 属性 操作

    /subsystem=jmx

类型:mail

类别:mail-server-security
  • requires-addressable: false
  • requires-read: false
  • requires-write: true
    PATH 属性 操作

    /subsystem=mail/mail-session=*/server=pop3

    username, tls, ssl, password

    /subsystem=mail/mail-session=*/server=imap

    username, tls, ssl, password

    /subsystem=mail/mail-session=*/custom=*

    username, tls, ssl, password

    /subsystem=mail/mail-session=*/server=smtp

    username, tls, ssl, password

类型:naming

类别:jndi-view
  • requires-addressable: false
  • requires-read: true
  • requires-write: true
    PATH 属性 操作

    /subsystem=naming

    jndi-view
类别:naming-binding
  • requires-addressable: false
  • requires-read: false
  • requires-write: false
    PATH 属性 操作

    /subsystem=naming/binding=*

类型:remoting

类别:remoting-security
  • requires-addressable: false
  • requires-read: true
  • requires-write: true
    PATH 属性 操作

    /subsystem=remoting/connector=*

    authentication-provider, security-realm

    /subsystem=remoting/remote-outbound-connection=*

    username, security-realm

    /subsystem=remoting/connector=*/security=sasl

类型:resource-adapters

Classification: resource-adapter-security
  • requires-addressable: false
  • requires-read: true
  • requires-write: true
    PATH 属性 操作

    /subsystem=resource-adapters/resource-adapter=*/connection-definitions=*

    security-domain, recovery-username, recovery-security-domain, security-application, security-domain-and-application, recovery-password

类型:security

类别:misc-security
  • requires-addressable: false
  • requires-read: true
  • requires-write: true
    PATH 属性 操作

    /subsystem=security

    deep-copy-subject-mode

类型:web

类别:web-access-log
  • requires-addressable: false
  • requires-read: false
  • requires-write: false
    PATH 属性 操作

    /subsystem=web/virtual-server=*/configuration=access-log
类别:web-connector
  • requires-addressable: false
  • requires-read: false
  • requires-write: false
    PATH 属性 操作

    /subsystem=web/connector=*
类别:web-ssl
  • requires-addressable: false
  • requires-read: true
  • requires-write: true
    PATH 属性 操作

    /subsystem=web/connector=*/configuration=ssl
类别:web-sso
  • requires-addressable: false
  • requires-read: true
  • requires-write: true
    PATH 属性 操作

    /subsystem=web/virtual-server=*/configuration=sso
类别:web-valve
  • requires-addressable: false
  • requires-read: false
  • requires-write: false
    PATH 属性 操作

    /subsystem=web/valve=*

第 12 章 事务子系统的配置

12.1. JTS 事务

12.1.1. 为 JTS 事务配置 ORB

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

注意

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

过程 12.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 部分。

例 12.1. 启用安全拦截器

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

例 12.2. 启用 JTS 里的 ORB

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

例 12.3. 在 JacORB 子系统里启用事务

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

例 12.4. 在事务子系统里启用 JTS

/subsystem=transactions:write-attribute(name=jts,value=true)

12.1.2. JMS 配置

12.1.2.1. 对 HornetQ 配置属性的引用

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

例 12.5. 示例

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

表 12.1. HornetQ 属性

属性 示例值 类型
allow-failback true BOOLEAN
async-connection-execution-enabled true BOOLEAN
backup false BOOLEAN
cluster-password somethingsecure STRING
mask-password true BOOLEAN
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 的值必须比发往服务器的消息大小要大,否则服务器无法存储这个消息。

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

13.1. 配置 mod_cluster 工作节点

总结

mod_cluster 工作节点(Worker Node)由一个 JBoss EAP 服务器组成。这个服务器可以是受管域服务器组的一部分,或者是一个独立服务器。在 JBoss EAP 里会有一个单独的进程运行,它管理群集里的所有节点。它被称为主节点。关于工作节点的更多概念,请参考《红帽 JBoss EAP 6.1 管理和配置指南》里的『工作节点』章节。关于 HTTPD 负载平衡的概述,请参考 《JBoss EAP 6.1 管理和配置指南》里的『HTTP 连接器概述』章节。

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

工作节点配置

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

注意

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

过程 13.1. 配置工作节点

  1. 配置网络接口。

    在默认情况下,网络接口都是 127.0.0.1。每个容纳独立服务器或服务器组里的一个或多个服务器的物理主机的接口都需要进行配置以使用其他服务器可以看到的公共 IP 地址。
    要修改 JBoss EAP 6 主机的 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/bin/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
          你可以在本指南的『敏感字符串的密码阀』里找到更多信息:第 10.11.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. 重启服务器。

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

你的独立服务器,或者位于受管域的服务器组里的服务器,现在已被配置为 mod_cluster 工作节点。如果你部署一个群集应用程序,它的会话会被复制到所有的群集节点以用于失效切换,且它可以从外部的 HTTPD 服务器或负载平衡器接受请求。在默认情况下,群集的每个节点都可以用自动发现来发现其他节点。要配置自动发现和 mod_cluster 子系统的其他专有设置,请参考《管理和配置指南》里的『配置 mod_cluster 子系统』章节。要配置 Apache HTTPD 服务器,请参考《管理和配置指南》里的『将外部 HTTPD 用作 JBoss EAP 应用程序的 Web 前端』章节。

第 14 章 安装补丁

14.1. 关于补丁和升级

JBoss EAP 6 里的补丁机制应用专有 '次要' 版本的更新,如 JBoss EAP 6.2。补丁可以包含一次性的、安全性或积累更新。
主要版本和次要版本间的升级(例如从 6.1 到 6.2)需要不同的过程来完成。

14.2. 关于补丁机制

JBoss 补丁是以两种方式发布的。
  • 异步更新(Asynchronous update):作为一次性的补丁,它在现有产品的常规更新周期之外发布。它可能包含安全补丁、红帽全球支持服务(GSS)提供的用以修复特定问题的其他一次性补丁。
  • 计划中的更新(Planned update):它包括现有产品的累积补丁、小版本、次要版本或主要版本升级。累积补丁包括为该产品版本开发的所有异步更新。
决定是否以计划中更新或异步更新发布补丁取决于要修复的漏洞的严重性。影响低的漏洞通常会推迟到下一个累积补丁或次要版本里解决。而中等或影响更严重的漏洞则通常按照严重性在只包含相应解决方法的异步更新里解决。
JBoss 产品的累积补丁和安全补丁以两种形式发布:ZIP(对于所有产品)和 RPM(对于产品的子集)。

重要

JBoss 产品必须一直使用同一种补丁方法来更新:ZIP 或 PRM。
JBoss 产品的安全更新是由勘误(ZIP 和 RPM 方法皆可)提供的。勘误封装了要解决的漏洞的列表、严重性级别、受影响的产品、漏洞的文本描述、对补丁的引用。程序错误修复的更新是不通过勘误来通知的。
关于红帽对 JBoss 安全漏洞评级的更多信息,请点击:第 14.6 节 “JBoss 安全补丁的严重性和影响级别”
红帽还维护了一个通知订阅者安全漏洞的电子邮件列表。 第 14.3 节 “订阅补丁邮件列表(Patch Mailing List)”

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

总结

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

前提条件

过程 14.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 补丁邮件列表来接收安全相关的通知。

14.4. 以 ZIP 形式安装补丁

14.4.1. patch 命令

patch 命令用来将下载的 ZIP 补丁应用到单个 JBoss EAP 6 服务器实例。它无法在整个受管域里自动应用补丁,而只能对其中单独的服务器实例应用补丁。

重要

用 RPM 方法安装的 JBoss EAP 6 服务器实例无法使用 patch 命令进行更新。关于更新用 RPM 安装的 JBoss EAP 6 服务器,请参考 第 14.5 节 “以 RPM 形式安装补丁”

注意

patch 只能用于 JBoss EAP 6.2 和更高版本。对于 6.2 版以前的补丁,你应该参考相关版本的文档:https://access.redhat.com/site/documentation/
除了应用补丁,patch 命令可以给出安装的补丁状态的基本信息,并提供立即回滚应用程序补丁的途径。
在启动补丁应用程序或回滚操作前,patch 工具将检查它根据用户修改而更新的模块和其他杂项文件。如果检测到用户的修改,且没有指定 conflict-handling 开关,patch 工具将中止操作并发出冲突警告。这个警告将包括一个有冲突的模块和其他文件的列表。要完成这个操作,patch 命令必须带有指定如何解决冲突的开关来重新运行:要么保留用户的修改,要么进行覆盖。

表 14.1. patch 命令的参数和开关

参数或开关 描述
apply 应用补丁。
--override-all 如果有冲突,补丁操作将覆盖任何用户所作的修改。
--override-modules 如果任何已修改的模块有冲突,这将用补丁操作里的内容覆盖这些修改。
--override=path(,path) 只用于指定的杂项文件,它经用补丁操作里的文件覆盖有冲突的已修改的文件。
--preserve=path(,path) 只用于指定的杂项文件,它保存有冲突的已修改的文件。
info 返回当前安装的补丁的信息。
rollback 回滚应用程序的补丁。
--reset-configuration=TRUE|FALSE 进行回滚必须设置的选项,它指定是否将回复服务器配置文件作为回滚操作的一部分。

14.4.2. 用 patch 命令安装 ZIP 形式的补丁

总结

这个任务描述了如何使用 patch 命令以 ZIP 方式来安装 JBoss EAP 6。

重要

patch 命令是 JBoss EAP 6.2 里添加的功能。对于 6.2 之前的版本,安装 ZIP 格式的补丁的过程是不一样的,你可以参考相关版本的文档:https://access.redhat.com/site/documentation/

前提条件

  • 对红帽客户门户的有效访问和订阅。
  • 对以 ZIP 形式安装的 JBoss 产品的当前订阅。
  • 对要更新的服务器实例的管理 CLI 的访问。请参考《管理和配置指南》里的 《登陆管理 CLI》

过程 14.2. 使用 patch 命令应用 ZIP 补丁到 JBoss EAP 6 服务器实例。

警告

在安装补丁之前,你应该备份 JBoss 产品以及所有自定义的配置文件。
  1. 从红帽客户入口 https://access.redhat.com/downloads/ 下载补丁 ZIP 文件。
  2. 在管理 CLI 里,用下列命令及补丁文件的合适路径来应用补丁:
    [standalone@localhost:9999 /] patch apply /path/to/downloaded-patch.zip
    如果在试图应用补丁时有冲突,patch 根据将显示警告。关于使用命令开关重新运行命令以解决冲突的信息,请参考 第 14.4.1 节 “patch 命令”
  3. 重启 JBoss EAP 6 服务器实例以使补丁生效:
    [standalone@localhost:9999 /] shutdown --restart=true
结果

JBoss EAP 6 服务器实例更新了最新的补丁。

14.4.3. 回滚用 patch 命令安装 ZIP 形式的补丁

总结

这个任务描述了如何使用 patch 命令回滚以 ZIP 方式来安装了补丁的 JBoss EAP 6。

警告

patch 命令回滚应用程序补丁的目的不是作为一个普通的写在功能。它的目的是在安装补丁后出现意外后果时立即使用。

重要

patch 命令是 JBoss EAP 6.2 里添加的功能。对于 6.2 之前的版本,回滚 ZIP 格式的补丁的过程是不一样的,你可以参考相关版本的文档:https://access.redhat.com/site/documentation/

前提条件

  • 之前使用 patch 命令应用的补丁。
  • 对服务器实例的管理 CLI 的访问。请参考《管理和配置指南》里的 《登陆管理 CLI》

过程 14.3. 使用 patch 命令从 JBoss EAP 6 服务器实例回滚 ZIP 补丁。

  1. 在管理 CLI 里,用 patch info 命令来找到要回滚的补丁的 ID。
    • 对于累积补丁,补丁 ID 是 patch info 输出里的第一个 cumulative-patch-id 的值。
    • 一次性安全补丁或程序错误修复补丁的 ID 会显示为 patch info 输出里的第一个 patches 的值,而最近应用的一次性补丁将首先列出。
  2. 在管理 CLI 里,用前面步骤里得到的补丁 ID 来进行回滚。

    警告

    请小心使用 --reset-configuration 开关。
    如果设置为 TRUE,补丁回滚过程也会将 JBoss EAP 6 服务器配置文件回复到应用补丁之前的状态。在应用补丁之后对配置文件的修改都会丢失。
    如果为 FALSE,服务器配置文件不会被回滚。在这种情况下,服务器可能在回滚后无法启动,因为补丁可能已经修改了配置文件(如命名空间)而导致它不再有效,你需要手动进行修复。
    [standalone@localhost:9999 /] patch rollback PATCH_ID --reset-configuration=TRUE
    如果在试图回滚补丁时有冲突,patch 根据将显示警告。关于使用命令开关重新运行命令以解决冲突的信息,请参考 第 14.4.1 节 “patch 命令”
  3. 重启 JBoss EAP 6 服务器实例以使补丁回滚生效:
    [standalone@localhost:9999 /] shutdown --restart=true
结果

JBoss EAP 6 服务器实例已回滚了补丁及服务器配置文件(可选)。

14.5. 以 RPM 形式安装补丁

总结

JBoss 补丁以两种形式发布:ZIP(用于所有产品)和 RPM(用于产品的子集)。本节描述了使用 RPM 形式安装补丁的步骤。

前提条件

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

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

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

警告

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

    重要

    在更新 RPM 安装时,你的 JBoss 产品将用发布的所有 RPM 补丁进行累积更新。
结果

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

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

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

表 14.2. 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)。

例 14.1. CVSS v2 影响分数

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

14.7. 管理部署在 JBoss EAP 上的应用程序内部捆绑的依赖关系的更新

红帽为作为 JBoss EAP 安装的一部分的所有组件提供了安全补丁。然而,许多 JBoss EAP 的用户部署捆绑自己的依赖关系的应用程序,而不是只使用作为 JBoss EAP 安装的一部分的组件。例如,部署的 WAR 文件可能在 WEB-INF/lib/ 目录里包含依赖关系 JAR。这些 JAR 超出了红帽提供的安全补丁的范围。管理部署在 JBoss EAP 上的应用程序内部捆绑的依赖关系的安全补丁是这些应用程序的维护者的责任。下面的工具和数据源可能提供一些帮助,但不享有任何支持或 保障。

工具和数据源

JBoss 补丁邮件列表
订阅 JBoss 补丁邮件列表将让你知晓 JBoss 产品里已修复的安全漏洞,允许你检查所部署的应用程序是否捆绑了受影响组件的易受攻击的版本。
用于绑定的组建的安全顾问页面。
许多开源都有自己的安全顾问页面。例如,Struts 2 是常用的带有许多已知安全问题、且不属于 JBoss EAP 安装的一部分的组件。Struts 2 项目维护着一个上游安全顾问页面,如果你的应用程序捆绑了 Struts 2,你应该注意这个页面。许多商用的组件也维护着安全顾问页面。
经常扫描你部署的应用程序里已知的漏洞
你可以使用几个常用的商业工具。红帽员工开发了一个名为 Victims 的开源工具,但并不为此提供支持和保障。Victims 为几个构建和集成工具提供插件,它自动扫描应用程序里捆绑的已知的易受攻击的依赖关系。这些插件可用于 Maven、Ant 和 Jenkins。关于 Victims 工具的更多信息,请参考 https://victi.ms/about.html

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

第 15 章 应用程序的安全性

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

总结

jboss-as-ee_1_1.xsd 引入了对描述符属性替换的有限控制。本节内容涵盖了配置基于描述符的属性替换所需的步骤。

前提条件

  • 启动 JBoss EAP 实例。
  • 启动管理 CLI。
基于描述符的属性替换标记具有的布尔值:
  • 如果设置为 true,属性替换将被启用。
  • 如果设置为 false,属性替换将被禁用。

过程 15.1. jboss-descriptor-property-replacement

jboss-descriptor-property-replacement 用于在下列描述符里启用或禁用属性替换:
  • jboss-ejb3.xml
  • jboss-app.xml
  • jboss-web.xml
  • *-jms.xml
  • *-ds.xml
jboss-descriptor-property-replacement 的默认值是 true
  1. 在管理 CLI 里,运行下列命令来确定 jboss-descriptor-property-replacement 的值:
    /subsystem=ee:read-attribute(name="jboss-descriptor-property-replacement")
  2. 运行下列命令来配置其行为:
    /subsystem=ee:write-attribute(name="jboss-descriptor-property-replacement",value=VALUE)

过程 15.2. spec-descriptor-property-replacement

spec-descriptor-property-replacement 用于在下列描述符里启用或禁用属性替换:
  • ejb-jar.xml
  • persistence.xml
spec-descriptor-property-replacement 的默认值是 false
  1. 在管理 CLI 里,运行下列命令来确认 spec-descriptor-property-replacement 的值:
    /subsystem=ee:read-attribute(name="spec-descriptor-property-replacement")
  2. 运行下列命令来配置其行为:
    /subsystem=ee:write-attribute(name="spec-descriptor-property-replacement",value=VALUE)
结果

成功地配置了基于描述符的属性替换标记。

15.3. 数据源安全性

15.3.1. 关于数据源安全性

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

例 15.1. 安全域示例

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

例 15.2. 密码阀示例

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

15.4. EJB 应用程序的安全性

15.4.1. 安全标识符

15.4.1.1. 关于 EJB 的安全标识符

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

15.4.1.2. 设置 EJB 的安全标识符

例 15.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>

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

要设置安全标识符为特定角色,请使用 <security-identity> 标签内部的 <run-as><role-name> 标签。
<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> 元素。

15.4.2. EJB 方法权限

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

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

15.4.2.2. 使用 EJB Method Permission

概述

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

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

例 15.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>
	

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

<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>

例 15.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>

例 15.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>

例 15.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>

15.4.3. EJB 安全注解

15.4.3.1. 关于 EJB 安全注解

EJB 使用安全注解来传递安全信息到部署者。其中包括:
@DeclareRoles
声明哪些角色是可用的。
@RolesAllowed, @PermitAll, @DenyAll
指定哪些方法权限是被允许的。关于方法权限的更多信息,请参考 第 15.4.2.1 节 “关于 EJB 方法权限(EJB Method Permission)”
@RunAs
配置组件的传播的安全标识符。
关于更多的信息,请参考 第 15.4.3.2 节 “使用 EJB 安全注解”

15.4.3.2. 使用 EJB 安全注解

概述

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

控制 EJB 的安全权限的注解

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

例 15.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 GoodbyeAdmin(String msg) {
		return "See you later, " + msg;
	}
}
在这段代码里,所有的角色都可以访问方法 WelcomeEveryone GoodBye 方法在进行调用时以 tempemployee 角色运行。只有 admin 角色可以访问方法 GoodbyeAdmin,以及其他任何没有使用安全性注解的的方法。

15.4.4. 对 EJB 的远程访问

15.4.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 6 里的多个子系统使用,它让你设计、实施和部署客户可以通过不同的传输机制来远程调用的服务。它也允许你访问 JBoss EAP 6 里现有的服务。
数据编码

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)。

15.4.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 方法。

15.4.4.3. 关于远程服务器检测

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

15.4.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 EAP 6 组件,如 EJB、ORB、JMS 提供者,在默认情况下都使用安全的接口。

警告

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

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

表 15.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 6 的子系统使用。对自定义连接器的元素和属性的设置取决于你的应用程序,更多的信息请联系红帽全球支持服务。

表 15.2. 连接器属性

属性 描述 CLI 命令
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 EAP 6 安装里 ApplicationRealm 是可用的。
/profile=default/subsystem=remoting/connector=remoting-connector/:write-attribute(name=security-realm,value=ApplicationRealm)

表 15.3. 连接器元素

属性 描述 CLI 命令
sasl
用于简单验证和安全层(Simple Authentication and Security Layer,SASL)验证机制的封装元素
N/A
属性
包含一个或多个 <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 属性以用于授权。

表 15.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 计算强度值的列表,以降序排列。
/profile=default/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-anonymous,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)
属性
包含一个或多个 <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)

例 15.11. 配置示例

这个例子展示了 JBoss EAP 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="http://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 和多点传送的自动检测

15.4.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 到使用带有自定义远程连接器的服务器组里,如果你使用的不是受管域则部署到独立服务器里。

15.4.4.6. 添加新的安全区

  1. 运行管理 CLI。

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

    运行下列命令在域控制器或独立服务器上创建一个名为 MyDomainRealm 的安全区。
    /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)
结果

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

15.4.4.7. 添加用户到安全区里

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

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

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

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

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

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

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

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

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

总结

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

警告

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

过程 15.3. 为 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>
        
    	
    	
        
        
    	
    	
    
    
结果

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

例 15.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>

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

总结

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

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

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

第 16 章 单点登录(SSO)

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

概述

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

群集和非群集的 SSO

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

SSO 是如何工作的

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

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

SSO 的限制

不能在第三方边界传播数据
SSO 只能在部署在 JBoss EAP 6 容器里的应用程序间使用。
只能用于容器管理的验证
你必须在应用程序 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 应用程序里一致。
所有的安全集成必须接受相同的凭证(如用户名和密码)。

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

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

16.3. 选择正确的 SSO 实现

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

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

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

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

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

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

16.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。请注意,这些命令修改了受管域的 ha 配置集。对于独立服务器,你可以修改这些命令来使用其他的配置集,或者去掉命令行的 /profile=ha 部分。

    例 16.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,表示子系统存在。否则,你需要添加它。

    例 16.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)

    例 16.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 没有在你的配置里出现。

    例 16.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 参数,它们控制持久数据的群集复制。

例 16.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>
		

表 16.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() 方法在程序里使会话失效。

16.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 环境里。

16.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 服务器。

16.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 地址和网络上主机和其他设备的名称之间的映射。

16.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 的类的映射。

表 16.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.

过程 16.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="UsersRoles" 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 功能。

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

17.1. 关于安全性扩展架构

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

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

JAAS 里的主要实现是 org.jboss.security.plugins.JaasSecurityManager,它实现了 AuthenticationManager RealmMapping 接口。基于对应的组件部署描述符的 <security-domain> 元素,JaasSecurityManager 集成到了 EJB 和 web 容器层。
关于 JAAS 的更多信息,请参考 第 17.2 节 “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 和其他加密的用例。

进一步的信息

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

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

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

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

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

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

应用程序专有的配置

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

表 17.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 6 里,容器端的组件是 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 的验证步骤

图 17.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 对象相关联。

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

概述

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

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

  1. 定义安全域

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

      安全域是在服务器配置文件的 security 子系统里配置的。如果 JBoss EAP 6 实例运行在受管域里,配置文件应该是 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,你可以在红帽的客户门户找到这些例子。

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

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

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

过程 17.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-constraint>	
    
    <security-role>
      <role-name>eap_admin</role-name>
    </security-role>
    
    
    <login-config>
        <auth-method>BASIC</auth-method>
    </login-config>
    			
    
    
    你需要指定验证方法,它可以是 BASIC, FORM, DIGEST, CLIENT-CERT, SPNEGO.。这个例子使用了 BASIC 验证。
  3. 在 WAR 的 jboss-web.xml 里指定安全域

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

例 17.1. 配置了基于角色的安全性的 web.xml 示例

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">

<display-name>Use Role-Based Security In Servlets</display-name>

<welcome-file-list>
  <welcome-file>/index.jsp</welcome-file>
</welcome-file-list>

<servlet-mapping>
    <servlet-name>DisplayOpResult</servlet-name>
    <url-pattern>/DisplayOpResult</url-pattern>
</servlet-mapping>

<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-constraint>

    <security-role>
      <role-name>eap_admin</role-name>
    </security-role>

    <login-config>
        <auth-method>BASIC</auth-method>
    </login-config>

</web-app>

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

你可以将第三方的安全系统集成到 JBoss EAP 6 里。这些系统通常是基于令牌的。外部的系统执行验证并通过请求头部信息将令牌传回 Web 应用程序。这通常被称为周边验证(perimeter authentication)。要配置周边验证,请添加一个自定义的验证 Valve。如果你有第三方的 Valve,请确保它位于 Classpath 并参考下面的例子以及第三方验证模块的文档。

注意

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

例 17.2. 基本的验证阀

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

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

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

<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 代码可以作为例子参考。

例 17.4. 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;
  }
}

第 18 章 移植

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

配置基本验证的安全性

在以前的 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

附录 A. 参考

A.1. 包括的验证模块

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

表 A.1. 登录模块

代码 分类 描述
客户端 分类 这个登录模块的目的是当 JBoss EAP 6 充当客户时建立调用者标识符和凭证。它不应该作为用于实际的服务器验证的安全域的一部分。
Remoting N/A Remoting 登录模块用于检查当前验证的的请求是否是从远程连接接收的,如果是则使用在验证过程中创建的标识符并和当前请求相关联。如果这个请求不是通过远程连接到达的,这个模块将不会做任何事情,并允许基于 JAAS 的登录继续,进入下一个模块。
RealmDirect N/A 如果当前的请求没有发生在 Remoting 登录模块里,RealmDirect 登录模块将使用安全区来进行验证,然后使用这个区来加载用户的角色。在默认情况下,这个等录模块假定要使用的区名为 ApplicationRealm,但也可以用其他名称覆盖。这个方法的好处是所有的存储配置都可以在区里完成,只需把安全域委托给安全区就可以了。

表 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 的区别是搜索用户角色的上下文可能对于每个用户都是唯一的。
roleAttributeID
属性
roles
包含用户角色的属性的名称。
roleAttributeIsDN
truefalse
false
roleAttributeID 是否包含角色对象的全限定 DN。如果为 false,角色名将从上下文名称的 roleNameAttributeId 属性值里获取。某些目录模式,如 Microsoft Active Directory,要求这个属性的值为 true
roleNameAttributeID
属性
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 的密码以明文存储,或者用 EXT 命令从外部夹在。这个密码可以用 Vault 机制进行加密。你可以使用下列格式:
  • {EXT}... 这里的 ... 是要求的外部命令。例如:{EXT}cat /mysecretpasswordfile
  • {EXTC[:expiration_in_millis]}... 这里的 ... 是传给 Runtime.exec(String) 方法来执行平台命令的命令。命令输出的第一行被用作密码。
    EXTC 缓存密码 expiration_in_millis 毫秒。 默认的缓存超时时间是 0,也就是无限期。
  • {CLASS}classname[:ctorargs] 里的 [:ctorargs] 是一个由 : 隔开的选项,它将传入 classname ctorctorargs 自身就是一个用逗号隔开的字符串列表。
    密码是通过调用 char[] toCharArray() 方法从 classname 里得到的,否则将使用 String toString()
参考下面的主题里关于加密敏感字符串的内容: 第 10.11.2 节 “创建一个 Java 密钥库来存储敏感信息”
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 的名称。
none
将和针对这个模块验证的任何主题关联的 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. SimpleUsers

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

表 A.30. SimpleUsers 模块选项

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

表 A.31. LdapUsers

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

表 A.32. 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.33. 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.34. SPNEGOUsers

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

表 A.35. SPNEGO 模块选项

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

表 A.36. AdvancedLdap

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

表 A.37. AdvancedLdap 模块选项

选项 类型 默认 描述
bindAuthentication
用于绑定到目录服务器的 SASL 验证的类型。
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.38. AdvancedADLdap

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

表 A.39. UsersRoles

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

表 A.40. 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。
自定义验证模块

验证模块是 javax.security.auth.spi.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 6 提供了下面的安全映射角色。
代码 分类
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.41. 常用的顶级属性

属性 描述
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.42. 注解配置元素

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

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

表 A.43. 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.44. 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.44 “EJB 安全参数元素” 里描述的每个标签。它们可以用在 <session> 里。
<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. 修订记录

修订历史
修订 1.0.0-1Wed Jul 02 2014CS Builder Robot
Built from Content Specification: 14876, Revision: 564067

法律通告

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.