
日期:2006-09-21 作者:喜腾小二 来源:PHPChina
作者 Kevin Kardasz、Vadim Kudryavtsev、Robert
Mark、Mikhail Seliverstov
麦吉尔大学的开发与校友关系部如何使用 PHP 和 Oracle 在网上支持多达 16
万名用户
在麦吉尔大学(位于加拿大魁北克省的蒙特利尔)的开发与校友关系部,我们正在使用行业级的 PHP 应用程序和
Oracle9i,将范围广泛的多层次服务转移到 Web 上,这些服务由 200 到 300 名受薪的和志愿的工作人员以及多达 16
万名校友和捐赠人使用。预计该程序在主要的电子广播时段每小时将会接纳数万名用户。我们利用 Oracle 功能强大的行级安全特性以及 PHP
的高性能和快速开发平台来完成这一工作。本文将探讨以下方面:
背景
我们的机构面临着很多机构所面临的问题:
我们已经启动了安全的、基于 Web 的模块化数据库应用程序开发的第一阶段。它以 PHP 和
Oracle9i 编写而成,在替代卫星应用程序及 Web
站点之后,必将为用户提供更好的服务。它对公共数据进行同步,并在总体上以及数据方面加强最佳业务惯例的应用。
该应用程序称为
Companion,因为它增加了反向中央数据库的功能,准确地报告中心数据,并在出现新数据时将其回送。
它包含超过 100 个表,这些表被分为 6 个模块。最长的表大约有一百万行。有些表有 20 到 30
列。全部用途是由用于行级安全性的 Oracle 细粒度访问控制 (FGAC) 和虚拟专用数据库 (VPD) 构成的。
Companion 应用程序包含三个域,而且能够容纳更多的域,这些域全都安全地访问同一 PHP/Oracle
应用程序中的相同数据。这些域包括以下内容:
Apache 和 Linux。二者都以强健、低成本和安全性(在正确配置的情况下)而着称。除了作为数据库服务器操作系统给人留下深刻印象之外,我们还发现 Linux
的 Debian 版本(来自 http://www.debian.org/ 的 Debian 项目)特别适合作为 Web
服务器,原因在于它在开放源代码社区中具有非常高的支持率、其集成软件的高质量以及它在稳定性方面的良好声誉。
PHP。我们没有选择 Active Server
Pages、ColdFusion 和 JSP,而是选择 PHP 作为我们的 Web 服务器端脚本环境,原因在于它的速度、成本效益、大量的库文件以及比其他 Web
技术更好的自定义功能。使用 PHP 还使我们免于束缚在特定厂商的开发工具集上。通过一个由 Web
网站以及程序员论坛(提供关于如何处理高级故障的有用提示)组成的网络,可以在 Web 上获得支持。有些 Web
网站还提供大量可用于开发更大型应用程序组件的有效代码。大部分程序员很容易理解 PHP 代码,因为它使用了与 C 相似的语法。我们讨论过,使用 Java
来开发我们所期望的服务器对象,其运行效率高于脚本代码。但是,我们尚未发现需要与 Java 集成,因为已经证明 PHP 非常灵活和快速,足以满足我们的需要。清单 1 显示了一个通过 PHP 查询 Oracle 的示例。
在进行更加复杂的面向对象的编程时,使用 PHP
的不足就会显现出来。这是因为它最初是作为一种脚本语言构建的,所以在 PHP 第 4 版中面向对象的特性非常少。这一问题已在 PHP 第 5
版中得以解决,该版本中引入了高级的面向对象特性。
Oracle9i。在不使用第三方工具的情况下,保持 Companion 数据库与中央数据库的同步是我们的首要需求之一。在两个
Oracle9i 系统之间交换数据将会证明比开发其他转换和传输数据的方法要容易得多。在我们的案例中,使用 Oracle9i带来的好处与我们的投入相比要大得多,这是因为我们能够利用先前 PL/SQL 代码,从而节省了开发时间。由于 Oracle 也移植到了 Linux
上,因此我们现在在 PC 体系结构的数据库服务器操作系统方面有了更多的选择。虽然中央数据库运行在 Oracle8i 上,但我们选择使用
9i 进行开发,以利用其更加先进的 VPD 特性(如 FGAC 和安全应用程序环境),它是市场上具有这些功能的唯一平台。
Companion 应用程序的结构
如同许多软件项目一样,我们开发团队的目标就是,基于可重用安全性组件和数据访问组件的框架,创建由多个功能独立的模块组成的灵活、可伸缩的应用程序。以下就是我们如何做到这一点的。
N 层体系结构。为了确保应用程序的可移植性和按需扩展的能力,需要对总的体系结构进行分层 —
换句话说,分为两层或多层,从而减少在某些基础性技术过时或者不能满足我们的需要时重新编写整个应用程序的风险。
在经过大量思考后,我们决定选用自定义的 n
层体系结构(该结构考虑了应用程序的需求以及机构的具体情况),而没有采用较传统的两层或三层的方法。(参见图 1。)
![]() |
GUI 呈现层是最终用户利用其本地浏览器实际看到并处理的内容。该层包含由底层生成的 HTML 页面和表单,还包括用来强化外观和导航的客户端
Javascript 和 CSS(层叠样式表)。该层的主要目的是收集用户输入和提供输出。
呈现逻辑层位于服务器上,只包含那些按功能逻辑分类的 PHP
脚本。它包含呈现模板和布局、验证例程以及特定模块专用的脚本。该层主要用于验证和处理用户输入,并基于从用户和更低层次接收的数据准备输出。
业务逻辑层是实施大部分业务规则的层次(由于安全原因,将某些关键规则严格限制在数据库级,而没有暴露给应用程序级)。它提供了 PHP
类的集合,这些 PHP
类封装了系统各部分专用的规则。此外,该层包含安全框架。该框架通过提供用户验证、会话管理和请求授权等机制,在应用程序级上管理用户的访问。特别是,该框架提供了很多公共方法,允许较高的层次基于安全权限对用户接口进行自定义。最后,该层包含可重用的通用类。应用程序使用这些类来进行数据加密、压缩、数据类型转换等。
数据抽象逻辑层由一组通用方法构成,用于为应用程序提供与存储在数据库中的实际数据的接口。换句话说,它在数据层与系统的其余部分之间提供了一个额外的抽象级别,这使应用程序真正独立于数据库引擎。在经过大量测试后,我们选择了另一种开放源代码工具
ADODB 库 (http://php.weblogs.com/) 来提供这种功能。
数据层又称 Oracle,无疑是该系统中最重要且最复杂的部分。其安全机制将在以下的安全章节中进行解释。
在图 2 中描述了
Companion 应用程序的物理布局:
![]() |
模块。每个模块均由数据结构(表和视图)和
PL/SQL 程序包的组成。结构与程序包都通过业务逻辑层中的 PHP 类与 GUI
应用程序相集成,以提供一系列主题统一的功能。模块化结构不仅使开发和组织功能的工作变得合乎逻辑且一致,而且对安全性也是一样。
模块在逻辑上是相互分离的。每个模块都拥有一套独有的安全策略,在 Web 服务器上拥有自己的装满其 PHP
应用程序文件的文件夹,并且拥有自己的 GUI
子站点和表集。每个表名以两个表示该表所属模块的字母作为前缀。文件、策略、过程以及其他存储的代码集均遵循一种命名惯例,这种惯例将它们标识为特定模块的一部分。
在某种意义上,模块是相互分离的,但每个模块都嵌入到更大的应用程序中。模块共享相同的导航元素和公用数据,并且全都重用业务逻辑层提供的类。
| 表 HR_Employee c1_id c1_emp_category web_emp_photo c2_emp_email ------- ---------------- -------------- ------------ M290171 central staff (blob) james.milton@mcgill.ca C179022 central staff (blob) doris.seagal@mcgill.ca M109022 casual (blob) luke.grande@mcgill.ca C390101 work-study (blob) patrick.roy@mcgill.ca M203400 faculty based staff (blob) mcmaster.philip@mcgill.ca 表和字段的前缀码: HR — 人力资源模块(每个模块的每个表都有两个字母的前缀) C1 — 中央数据库 1* C2 — 中央数据库 2* WEB — Companion 数据库应用程序* * 字段的前缀依据其源数据库确定。 由于该应用程序是 companion 数据库, 它同步并使用来自于不同来源的数据, 尤其是来自于主要中央数据库的数据。 |
虽然大部分模块遵守相似的安全结构和原则,但是在项目开始的时候,我们的每个模块都需要有专门针对其需求的安全方案。共同安全原则的一个例子是指导者原则。为每个模块大致分配三位指导者。指导者通常是符合条件的非技术性用户。他们在其模块内拥有完全的管理权限,并可以向其他较低安全级的管理员分配权限。不同安全原则的一个示例是 HR
模块中保密信息原则。当员工访问其保密信息时,他们的行级安全依赖于其实体 ID。但是在大多数其他模块中,他们的行级安全依赖于其工作的职位 ID。
安全性
如果我们的数据非常敏感,则通过传统的基于 cookie
的会话机制在应用程序级上限制访问就不足以保证安全性。对于具有复杂的访问规则并且预期具有庞大流量的数据库而言,在管理行级安全的同时获得良好性能的最佳方法是将大部分安全功能
— 那些用于强化数据限制的特定规则的安全功能 — 转移到数据库级。
通过 VPD
保护数据库引擎级上的数据访问的安全而将访问控制放在了最低级别上,从而确保入侵者的查询工具(能够越过应用程序安全机制)无法获得数据。此外,它为机构提供了更低的拥有成本:在数据服务器上一次性构建安全性,而不用将安全性构建到所有访问数据的应用程序工具中。最后,它集中了数据访问的管理和控制。
Companion 利用了 Oracle
传统的基本角色安全和用户帐户安全功能。更重要的是,它使用了两个功能强大的 VPD 工具:安全应用程序环境和 FGAC。(有关 VPD 的详细说明,参见 otn.oracle.com/deploy/security/oracle9ir2/pdf/VPD9ir2twp.pdf)。使用这四个安全性概念作为框架,让我们来探讨我们的业务模型如何与
Oracle 相融合。
基本用户帐户安全。安全性和数据库体系结构是围绕一个每天从中央数据库进行更新的主要名称列表而设计的。这些名称被称为实体而非用户。
Companion 包含两个用户组,工作人员和毕业生。用户组被 Oracle
的基本安全功能看作单个帐户。举例来说,这意味着 3
万个并发用户只共享两个帐户。其他用户(包括某些实体和所有的非实体)无法共享任何帐户,但它们仍然可以访问未加限制的公共数据。(注意:在本文中,用户指访问系统的人,而帐户指 Oracle 基本用户帐户。)如图 3 所示,用户是公共实体和成员的子集。
![]() |
那么为什么仅为所有经过验证的用户分配两个共享帐户,而不是分别为每个用户分配一个帐户?因为对许多应用程序安全性的管理,需要依据用户可能赋予这些应用程序的各种属性进行,而非依据用户所拥有的帐户进行。可完成此目标的工具将在以下关于
VPD 的章节中进行探讨。
基本角色安全。当用户通过单击某个链接访问
Companion 应用程序时,从安全性的观点来看,用户是在访问模块。每个模块都与一个 Oracle
安全角色相关联,该角色被分配或被拒绝赋予每个用户帐户。这些安全角色管理表、视图、过程和函数等的对象级安全。基本角色安全只是将广泛的用户权限(选择、插入、执行等)分配给对象,它是建立访问的第一步。特定的用户权限由
FGAC 控制。
VPD 安全应用程序环境。Companion
应用程序使用 Oracle 的安全应用程序环境,将实体 ID 分配给登录的用户。利用两个简单的标识符 — 帐户(工作人员或毕业生)和环境(实体 ID)—
即可确立大部分的访问规则,并且很容易派生出其余的规则。
这些用于用户登录会话的标识符由一个加密的安全令牌所携带,每次进行查询(即点击数据库中某项内容的超链接)时都会读取该令牌。在加密令牌的会话持续期间,用户登录(名称/口令)以及实体
ID 都存储在 Web 服务器上。登录用于建立与数据库的连接并获得对数据结构的访问权限,而实体 ID
则用于通过安全策略将数据访问权限细化到在行级。
VPD 细粒度访问控制。使用由安全应用程序环境提供的实体 ID 标识符,Oracle 安全策略能够在行级管理访问。这些 FGAC 安全策略 —
与基本角色安全一样 — 附加在对象上。与基本角色安全不同的是,FGAC 策略进一步将访问权限限制在与实体 ID
关联的属性上。附加到所有非公共数据库对象上的安全策略有助于完全剥离受限制的数据。因此,即使数据库登录名和口令被盗取,对任何受策略保护的表进行查询的企图(使用标准
SQL
工具)都将得到空记录集或有限的记录集。策略对用户的属性以及行的属性进行鉴定,并确定用户可以看到哪些行(如果有的话)。例如,只有那些住在温哥华并应邀出席西海岸地区经理宴会的实体才可以查看出席同一活动的登记者名单。另一个例子:分配到
Web 站点的编辑人员不能删除网站的页面,但该网站的作者可以这样做。
共性。我们的 160,000
个实体构成了一个变化的复杂群体。每个实体都有很多共性,如年龄、地位、财政群体、地区等等。为了便于没有经验的管理员分配访问权限,已经将许多这样的属性汇总到一系列的共性组中。有一个共性查询表每天都进行更新,并列出所有实体及其相应的共性。FGAC
也使用该表管理对那些已经分配了共性的数据行的访问。共性还用于为站点的访问者灵活地提供个性化的内容。
图 4描述了在用户调用页面时筛选数据请求的五个步骤。
![]() |
使存储在 Companion
数据库中的数据与存储在中央数据库中的数据保持同步已变成了一项挑战,因为二者都在不断地更新。我们设计了一个系统,它比实时系统慢,但是保证符合严格的数据标准。
起初,每夜在 Companion 数据库中刷新由中央数据库提供的“同步”数据(参见图
5)。这一转储过程中最重要的对象是实体表,它构成了我们内部安全性的基础。
虽然该组同步表在 Companion
应用程序的模块中被频繁使用,但它们并不由这些模块进行更新。实际上,这些更新存放在保持表中,如果有必要,由记录工作人员使用专门设计的客户服务器接口对其进行仔细的编辑。然后,更新的数据在经过编辑从而符合标准之后,被回送到中央数据库。由于中央数据库和
Companion
数据库中的所有行都有日期时间标记,管理这一流程的程序仔细地检查相同记录的更新情况(这些更新可能相互冲突)。与以前相比,准确、更新的数据能够以更大的数量、在利用更少手动操作的情况下流向中央数据库。
由于我们的数据库是 Companion 数据库,它从一些其他的大型数据库中借用字段和数据。Companion
数据库中每个字段的名称都以一个字母的代码作为前缀,该代码表示其源数据库。由于强制在每个表的每个字段名中识别数据源,因此不会漏掉进行同步的机会。
![]() |
学到的教训以及对开发人员的建议
Kardasz 的团队从这一正在进行的项目中所学到的主要教训如下:
![]() |
我们认为,本文中所描述的开发模型的使用范围是无限的。总而言之,该模型包括了保持核心同步数据的伴随数据库、多层应用程序结构、半独立的模块式开发、为新模块提供框架的核心安全组件、全部范围的
Oracle 安全特性(特别是 FGAC)以及 PHP
编程的快速和可靠性。我们强大的中央数据库的功能、准确性和相关性得以增强,而在每次发布新的模块时,都在具有最少逻辑障碍的情况下为用户添加功能。总的说来,最令人满意的是,它为用户提供非常良好的服务并且技术支持很易于管理。