级别: 中级 Ivan Dubrov (dubrov@isg.axmor.com), 系统架构师, Axmor Artem Papkov (artem@us.ibm.com), 解决方案架构师, IBM James Smith (jamessmi@us.ibm.com), 经理和管理顾问, IBM
2005 年 8 月 15 日 开发安全的 Java 2 Platform, Enterprise Edition (J2EE) 应用程序是任何规模的企业必须考虑的事项。在本文中,IBM Advanced Technology Solutions 团队提供了对 Apache Geronimo 应用服务器安全特性的彻底概述,并展示了如何使用这些特性来保护企业应用程序。使用一个模拟银行场景的示例来学习如何为现实世界的 Geronimo 应用程序构建安全。
业务场景
现代企业通常是每天都面临大量机会和挑战的复杂系统。其中许多挑战可以让企业的 IT 基础设施面临风险,所以确保尽可能最高级别的 IT 安全是至关紧要的。Apache Geronimo 应用服务器支持中小企业应用程序,并为最新的 J2EE 规范提供健壮安全的支持。使用 Geronimo 应用服务器允许建立符合最新标准的安全的企业基础设施。为了演示 Geronimo 的安全特性,IBM 专家团队实现了一个简单银行场景的模拟器,以展示 Geronimo 在现实世界业务场景中的技术能力。
用作示例的银行业务场景实现了一个资金转帐用例。需要从帐户转帐资金的零售银行客户和监督所有交易的审计员(称作用户)可以访问应用程序。主要的安全假设是零售银行客户不能查看其他用户的帐户,而审计员可以访问应用程序正在执行的所有交易 —— 使审计员充当管理员或超级用户的角色。
应用程序允许零售银行客户:
- 登录以输入认证和帐户访问授权的凭证。
- 查看帐户信息。
- 通过选择资金金额和转帐目的地来启动转帐。
- 检查已启动转帐的状态和历史。
应用程序允许银行审计员:
- 登录以作为超级用户输入认证和授权的凭证。
- 检查系统正在处理的所有交易。
图 1 所示的屏幕截图展示了启动资金转帐时显示给零售银行客户的界面。
图 1. 资金转帐启动界面
本文重点介绍保护机密信息和防止中间人(man-in-the-middle) 攻击,在这种攻击中,入侵者截取并修改服务器和客户机之间的流量。交易机密性通过认证和授权设施来实现。传输层和应用层都有这些设施。HTTPS 用作传输层的认证和授权设施,J2EE 安全用作应用层的认证和授权设施。
图 2 展示了应用程序的高级系统视图。
图 2. 应用程序的系统视图
如 图 2 所示,客户机组件表示一个具有用于访问应用程序的 Web 浏览器的计算机。客户机通过 HTTPS 协议与 Web 接口交互。该协议用于提供安全信息(比如用户凭证和交易信息)的机密性,还提供了防止中间人攻击的保护。对特定页面的访问使用 J2EE 声明性和程序性安全来限制,以禁止对页面的非授权访问。
Web 接口由多个 Web 页面和 Java servlet 组成。Servlets 使用 Enterprise JavaBeans (EJB) 调用将所有请求重定向至业务逻辑层。所有业务逻辑 EJB 方法使用 J2EE 声明性和程序性安全来保护,以在它们被不是应用程序 Web 接口的一些组件调用时禁止未授权访问。
业务逻辑组件由无状态 EJB 和多个数据 JavaBeans 组成。无状态 EJB 使用本地 EJB 调用来访问持久层,因此实体 bean 的方法不用 J2EE 安全来保护。
持久层由多个 Container-Managed Persistence (CMP) EJB 组成。通过 Java Database Connectivity (JDBC) 访问数据库。数据库访问安全依赖于 JDBC 驱动程序和数据库实现。出于本文目的,数据库管理系统与应用服务器并置。因此,应用程序假设对数据库进行独占访问。
解决方案中使用的数据库服务器被部署到嵌入 Geronimo 中的 Apache Derby 数据库管理系统中(参阅 参考资料 以链接到 Apache Derby 站点)。数据库包含业务信息(比如帐号),以及安全相关数据(比如用户凭证)。
设置 Geronimo 应用服务器安全
本节介绍配置 Geronimo 应用服务器安全需要遵守的步骤。其中包括设置 HTTPS 支持,以及配置 J2EE Web 和 EJB 安全。
HTTPS 配置
作为第一步,需要配置 Web 容器的 HTTPS 传输。HTTPS 是 HTTP 协议在 Secure Sockets Layer/Transport Layer Security (SSL/TLS) 协议上的实现(参阅 参考资料 以获得更多信息)。
解决方案使用 HTTPS 传输来保护在服务器和客户机之间传递的重要信息,比如用户凭证。因为 Geronimo 应用服务器的最新快照预配置有 HTTPS 传输,所以只需将正确的证书放置到 Geronimo 的证书存储中。Geronimo 发行版提供的默认证书是自签发的,可用于应用程序开发中。
有关证书的更多信息,请参阅 Sun Java keytool 实用程序的手册(参阅 参考资料)。将证书文件放置在 Geronimo 安装的 var/security 子目录中。
配置一般安全
配置完 HTTPS 传输之后,必须在特定于 Geronimo 的部署描述符(部署计划)中配置安全。这可以通过更新应用程序范围的部署计划、EJB 模块部署计划或 Web 模块部署计划来实现。
首先,声明应用程序角色和特定主体之间的映射。对于每个安全角色,必须声明相应的主体类及其名称。清单 1 中的代码示例提供了此类配置的一个示例。
注意 清单 1 中的下列内容:
- 名称为
auditor 的主体 org.apache.geronimo.security.realm.providers.GeronimoGroupPrincipal 映射到审计员应用程序角色。
- 名称为
user 的主体 org.apache.geronimo.security.realm.providers.GeronimoGroupPrincipal 映射到用户应用程序角色。
- 名称为
system 的主体 org.apache.geronimo.security.realm.providers.GeronimoUserPrincipal 在用户不进行认证时用作默认主体。
配置安全领域
下一步是配置 安全领域,安全领域定义用户凭证(用户名和密码)与用户角色之间的映射。角色稍后用于检查用户是否被授权访问特定资源(比如查看 Web 页面)。
配置安全领域需要配置三个 GBeans(参阅 参考资料 以链接到 GBeans 的说明):一个用于登录模块,一个用于登录模块使用,一个用于领域本身。
该登录模块提供了特定部分的认证。清单 2 展示了配置 SQL 登录模块的示例。
在 清单 2 中,workdev-sql-login 是一个登录模块,被配置为使用 SQL 数据库作为凭证来源。指定了两个 SQL 查询 —— 一个用于选择用户和相应密码的列表,另一个用于选择用户名和组名之间的映射。
使用 JDBC URL 中指定的 JDBC 驱动程序访问数据库。Geronimo 还提供了 Apache Derby,这是一个关系数据库,在示例应用程序中用来存储用户凭证。一定要注意,解决方案以两种不同的方法访问其数据库 —— 使用被部署为应用程序一部分的 DataSource,从 SQL 登录模块访问和从 EJB 模块访问。因为单个 Derby 数据库实例被限制为只允许通过由同一个类装载器装载的 JDBC 驱动程序进行的同时访问,所以数据库配置和 SQL 登录模块配置必须部署为单个应用程序的各个部分。
另一个 GBean 被配置来指定登录模块用于领域中。该 GBean 列出登录模块,并声明所需的登录模块。清单 3 展示了此类配置的一个示例:
清单 3. 登录模块配置
<gbean name="workdev-sql-modules"
class="org.apache.geronimo.security.jaas.JaasLoginModuleUse">
<attribute name="controlFlag">REQUIRED</attribute>
<reference name="LoginModule">
<name>workdev-sql-login</name>
</reference>
</gbean>
|
controlFlag 属性指明该登录模块用于为要认证的用户返回有效结果。LoginModule 属性引用指向登录模块配置。
另一个 GBean 定义安全领域本身。配置包括指定领域名称和引用 GBean,GBean 用于配置构成领域的登录模块列表。清单 4 展示了此类配置的一个示例。
清单 4. 定义领域
<gbean name="workdev-sql-realm"
class="org.apache.geronimo.security.realm.GenericSecurityRealm">
<attribute name="realmName">WorkdevRealm</attribute>
<reference name="LoginModuleConfiguration">
<name>workdev-sql-modules</name>
</reference>
</gbean>
|
完成这些配置步骤之后,可以配置 J2EE 安全设施来保护应用程序组件。
配置 Web 层 J2EE 安全
Geronimo 支持 Web 层的声明性和程序性安全。下文介绍这两种安全。
声明性安全
声明性地保护 Web 层意味着显式指定需要用户认证的页面。
要使用声明性安全方法,必须在 web.xml 部署描述符中指定所有可能的安全角色。清单 5 展示了此类规范的一个示例。
清单 5. 声明性安全
<security-role>
<description>Usual user</description>
<role-name>user</role-name>
</security-role>
|
security-role XML 元素包含单个安全角色的声明。该描述元素包含角色的文本描述,而 role-name 元素定义角色名称本身。
声明之后,角色可用于限制对特定 Web 页面的访问。这通过 Web 部署描述符中的 security-constraint 元素来实现。该元素描述要保护的页面集合和关联的安全约束。清单 6 展示了一个使用 /user/* URL 掩码保护所有页面的示例。
清单 6. 指定安全约束
<security-constraint>
<web-resource-collection>
<web-resource-name>UserArea</web-resource-name>
<description>User accessible pages</description>
<url-pattern>/user/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<description></description>
<role-name>user</role-name>
</auth-constraint>
</security-constraint>
|
web-resource-name 元素包含 Web 资源的名称。description 元素包含集合的文本描述。web-resource-collection 元素指定要保护的页面的模式,这是集合定义中最重要的信息。
auth-constraint 元素用于指定对通过认证的用户帐户的约束,这些约束必须在访问受保护页面之前得到满足。该元素包含约束描述和角色名称(先前在部署描述符中定义过)。
程序性安全
在 Geronimo 中实现的程序性安全的工作方式与 J2EE 1.4 规范中描述的一样。但是,目前它包含一些与传递安全上下文相关的设计缺陷,稍后将讨论。
在已实现的解决方案中,程序性安全用于实现成功授权后将用户重定向至相应页面的 servlet。该 servlet 的目标是验证用户角色是否已通过认证,并将审计员重定向至审计员页面。其他所有用户被重定向至转帐页面。
清单 7 展示了在 servlet 中实现的业务逻辑。
清单 7. 程序性安全示例
if(req.isUserInRole("user")) {
resp.sendRedirect("user/transfer");
} else if(req.isUserInRole("auditor")) {
resp.sendRedirect("auditor/viewOrders");
}
|
为了让安全实现正确发挥作用,servlet 被放到受保护区域,以便只有通过认证的审计员和用户可以访问。实现方法与上述让页面只能由用户访问的方法相似。
EJB 安全
Geronimo 支持 J2EE EJB 声明性和程序性安全。在本文描述的示例应用程序中,保护业务逻辑 EJB,以使只有通过认证的用户可以调用特定的 EJB 方法。这可以通过声明性或程序性安全来实现。
声明性安全
声明性地保护 EJB 方法按照 J2EE 规范进行。首先,在 EJB 部署描述符中声明安全角色,如 清单 8 所示。
清单 8. 声明安全角色
<security-role>
<description>User role</description>
<role-name>user</role-name>
</security-role>
|
然后,声明执行特定方法所需的权限。下列代码示例来自 EJB 部署描述符,如 清单 9 所示。
清单 9. 设置 EJB 方法的权限
<method-permission>
<role-name>user</role-name>
<method>
<ejb-name>BusinessLogic</ejb-name>
<method-intf>Remote</method-intf>
<method-name>getOwnOrders</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</method>
</method-permission>
|
在 清单 9 中,只有被授权的零售银行客户才能调用该方法。
对于 CMP EJB,它们的方法被标记为 unchecked,所以在调用期间不检查权限。清单 10 展示了这种配置的示例。
清单 10. 将 EJB 方法标记为 unchecked
<method-permission>
<unchecked/>
<method>
<ejb-name>User</ejb-name>
<method-name>*</method-name>
</method>
</method-permission>
|
该配置仍然安全,因为应用程序 CMP EJB 只提供了本地接口,因此只能从同一 EJB 容器中访问。(该过程从 2005 年 5 月 31 起不在 Geronimo 快照上工作。参阅 参考资料 以链接到 Apache 问题站点,其中提供了有关 JIRA 问题 661 和建议解决方案的详细信息。)
程序性安全
EJB 可以使用 EJBContext.getCallerPrincipal() 方法通过上下文获得调用者主体。但是,在撰写本文时,Geronimo 包含与该功能相关的设计缺陷。Geronimo 返回的 principal 类不包含 Web 容器中可用的所有安全信息。特别地,用户名不能由 principal 对象确定,虽然在 Web 容器中它是可用的。要解决这个问题,示例应用程序在调用 EJB 方法时显式传递用户名。(参阅 参考资料 以链接到 Apache 问题站点,其中提供了有关 JIRA 问题 668 的详细信息。)
结束语
本文描述了如何使用 Geronimo 应用服务器提供的安全设施来构建安全的应用程序。Geronimo 提供了以声明方式和编程方式在不同层次保护应用程序的大量设施。Geronimo 还提供了为中小企业构建安全应用程序的健壮功能。它使用最新标准并依赖 J2EE 规范来提供应用层安全设施。
但是,应用服务器的稳定版本还没有发布;因此,Geronimo 仍包含一些与 J2EE 支持有关的问题。其中一个问题与 EJB 容器有关,它无法确定一些类型的调用者主体信息,比如用户名。此外,值得注意的是,Geronimo 仍缺乏官方参考文档,因此快速查找信息有点困难。但是,因为 Geronimo 是开放源代码的,所以支持文档的缺乏通过开放源代码可用性和用户组支持得到了补偿。
总之,Geronimo 应用服务器具有已经演示的对 J2EE 标准的健壮支持,而且正在中小企业 IT 中寻找用武之地。
参考资料
作者简介  | |  | Ivan Dubrov 是俄罗斯新西伯利亚 IBM Advanced Technology Solutions (ATS) 实验室的软件工程师。他在新西伯利亚州大学以优秀成绩获得计算机科学理工学士学位。去年,他作为系统架构师和软件工程师参与了至少 5 个 ATS 项目。可以通过 dubrov@isg.axmor.com 与 Ivan 联系。 |
 | |  | Artem Papkov 目前是 IBM 的 Client Innovation Team 的解决方案架构师,他的工作致力于与客户和业务伙伴合作来采用新兴技术,比如 SOA、Web 服务及类似内容。1998 年毕业于 Belarusian State 大学的信息学和无线电电子学,获得计算机科学硕士学位。他在 2000 年加入了位于北卡罗莱纳州的 IBM Research Triangle Park。他的工作经验包括利用新兴技术的多层解决方案的软件开发、架构设计和基于因特网解决方案的集成。在过去三年中,他一直致力于紧密联系客户的工作,帮助他们采用 Web 服务作为 IBM 策略集成技术,并采用 SOA 作为集成方法。可以通过 artem@us.ibm.com 与 Artem 联系。 |
 | |  | Jim Smith 有 18 年的软件开发经验。他在加利福尼亚州利佛莫尔的 Sandia National Labs 开始了他的职业生涯,使用无数的现有遗留代码设计高速数据采集系统和分布式计算系统。凭着在 Java 语言和面向客户技术方面的扎实经验,Jim 转移到 Emerging Internet Technologies 团队,致力于为 IBM 客户实现 Java 解决方案。Jim 是 Advanced Technology Solutions (ATS) 的创始人之一,ATS 是一个全球软件服务和开发组织,致力于为 IBM、开发实验室、业务合作伙伴和客户开发、提炼并特许经营高级技术和轻量级业务过程,导致标准技术和 IBM 产品的快速采用和部署。目前,Jim 管理该组织。可以通过 jamessmi@us.ibm.com 与 Jim 联系。 |
对本文的评价
|