ホームページ  >  記事  >  php教程  >  Web サービスを使用して PHP5 で J2EE アプリケーションにアクセスする

Web サービスを使用して PHP5 で J2EE アプリケーションにアクセスする

WBOY
WBOYオリジナル
2016-06-21 08:58:43976ブラウズ

j2ee|php5|web|web サービス|プログラム|アクセス

多くの Web 開発者は PHP の豊富な機能と使いやすさを好みますが、場合によっては、J2EE アプリケーション サーバーにすでに存在するビジネス ロジックにアクセスする必要があります。この記事では、PHP 環境を離れたり、新しいプログラミング モデルを学習したりせずに、PHP 5 の新しい SOAP 拡張機能を介して Web サービスを使用して J2EE アプリケーションにアクセスする方法の例をいくつか紹介します。

PHP、Web サービス、SOAP の概要

この記事では、PHP スクリプトからエンタープライズ アプリケーションにアクセスする方法について説明します。あなたは PHP プログラマーで、本社から Web サービスとして提供されるサービスへのアクセスを提供する部門 Web アプリケーションのコードを記述する必要があるかもしれません。おそらく、あなたは経験豊富な J2EE 開発者で、PHP とそのアプリケーションについて詳しく知りたいと考えているかもしれません。この記事の例は、IBM WebSphere® アプリケーション サーバー上で実行される Enterprise JavaBean (EJB) ですが、この記事では Web サービスのデプロイメントについては説明しません。その主な目的は、さまざまな Web サービスの実装に適用できる、PHP から Web サービスの使用方法を紹介することです。

PHPとは何ですか?

PHP: ハイパーテキスト プリプロセッサ (PHP) は、動的な Web コンテンツを作成するための一般的なサーバー側スクリプト言語です。 PHP インタープリターは、ほとんどの Linux® バージョン、Windows®、Mac OS X、iSeries® などの主要なプラットフォーム用のソース コードまたはコンパイルされたバイナリを提供します。

PHP を実行している Web サーバーは文字通り何百万台もあり、そのほとんどが PHP 4 を使用しています。2004 年 7 月にリリースされた PHP 5 は徐々に採用されています。 PHP 5 ではオブジェクト モデルが改善され、基礎となるメモリ管理がマルチスレッドとパフォーマンスの観点から再設計されました。ただし、下位互換性のない変更がいくつかあることに注意してください。これらは PHP マニュアルに記載されています。

Webサービス技術とは?

Web サービスは、クライアントとサービスが疎結合された自己完結型のモジュール型アプリケーションを指します。 Web サービスの詳細については、この記事の目的上、主要なテクノロジのみを理解する必要があります。

SOAP (Simple Object Access Protocol) は、クライアントとサーバー間で受け渡されるメッセージを定義します。メッセージは XML 形式です。 SOAP は、プラットフォーム、プログラミング言語、ネットワーク、トランスポート層に依存しません。この記事では、SOAP over HTTP について説明します。

WSDL (Web サービス記述言語) は、Web サービスを記述するために使用される XML ベースの言語で、その記述にはサービスの場所、形式、操作、パラメーター、およびデータ型が含まれます。

UDDI (Unified description, Discovery, and Integration) は、API と UDDI レジストリ実装を使用して、ネットワーク上の Web サービス情報を保存および取得するための方法を提供します。

この記事には、SOAP メッセージと WSDL ドキュメントの例がいくつか含まれていますが、UDDI の例は提供されていません。

XMethods Web サイトは、さまざまなサーバー プラットフォームに実装されている公開されている Web サービスのリストを見つけることができる便利な Web サービス ツールです。この記事の例を使用すると、XMethods から選択したサービスに簡単にアクセスできます。

SOAP と PHP

PHP 4 スクリプトで SOAP を使用できるようにする製品はいくつかありますが、最も一般的なのは PEAR::SOAP と NuSOAP です。この記事の執筆時点では、これらの製品には PHP 5 との互換性にまだ問題があり、間もなくアップグレードされることが予想されます。

PHP 5 には、ext/soap と呼ばれる新しい組み込み SOAP 拡張機能があります。 PHP の一部として提供されるため、個別のパッケージをダウンロード、インストール、管理する必要はありません。これは、PHP for PHP ではなく C で書かれた最初の SOAP 実装であるため、作成者は、はるかに高速であると主張しています。

新しい拡張機能は PHP に不可欠な部分であるため、ドキュメントは PHP マニュアルの関数リファレンス セクションに含まれています。 SOAP リファレンスは重要な免責事項で始まります:

警告: この拡張機能は実験版です。この拡張機能の動作 (関数名やこの拡張機能に関するその他の内容を含む) は、将来の PHP バージョンでいつでも予告なく変更される可能性があります。この拡張機能は自己責任で使用してください。

この警告は少し気になるかもしれませんが、この拡張機能は実際には十分にサポートされているようです。他の新しいコードと同様に、拡張機能にもバグがありますが、報告された問題は通常、すぐに修正されます。欠陥のリストは PHP サイトで参照できます。この拡張機能は、PHP の将来のバージョンでは実験的な機能から主流の機能に移行すると推定されています。

PHP SOAP 拡張機能をインストールします

PHP 5 が Web サーバーにインストールされ、実行されている必要があります。私たちの実験では PHP 5.0.2 を使用しました。これは最新バージョンであり、PHP 5 の初期バージョンの多くのバグが修正されています。上で述べたように、ext/soap は PHP 5 の一部として提供されているため、個別にダウンロードする必要はありませんが、有効にするにはいくつかの変更を加える必要がある場合があります。必要な変更は、ソース コードをダウンロードしたか、PHP を自分でコンパイルしたか、バイナリを直接ダウンロードしたかによって異なります。

  如果下载的是 PHP 源代码,并在自己的平台上编译,那么可能需要重新进行构建,因为在默认情况下没有启用 ext/soap。重复以前的构建过程,并在 configure 命令中添加 --enable-soap 选项。

  如果下载的是预编译平台的二进制文件,ext/soap 可能已经编译但没有加载,因此需要更新 PHP 配置,以便加载 ext/soap。编辑 php.ini 并找到 Dynamic Extensions 部分,在这里增加一行代码来自动加载该扩展。

  在 Windows 上,这一代码行是:

extension=php_soap.dll

  在 UNIX 上是:

extension=php_soap.so

  如果以前没有加载过任何可选的扩展,可能还要设置 extension_dir 指令,让它指向包含扩展库(其中包括 php_soap)的目录,比如:

extension_dir="C:/php/ext/"(在 Windows 上使用正斜杠)

  不要将目录信息放到 extension 指令中,需要的话可以使用 extension_dir。

  对于 Windows,可以下载其他两个二进制包。Windows 安装程序包不含任何扩展,因此要使用 Windows zip 压缩包,这个压缩包中包含 ext/soap。

  注意,ext/soap 依赖于 GNOME xml 库,这个库必须使用 2.5.4 或更高版本。如果版本不够高,可以从 xmlsoft安装 libxml2。

  最后,ext/soap 在 php.ini 中有自己的配置部分,在完成配置之后,ext/soap 如下所示:

[soap]
; Enables or disables WSDL caching feature.
soap.wsdl_cache_enabled=1
; Sets the directory name where SOAP extension will put cache files.
soap.wsdl_cache_dir="/tmp"
; (time to live) Sets the number of second while cached file will be used
; instead of original one.
soap.wsdl_cache_ttl=86400


  这段配置控制了 SOAP 扩展的 WSDL 缓存特性。默认情况下,WSDL 描述文件在 24 小时(86400 秒)内都缓存在 /tmp 目录下。我们迟些时候再讨论这些内容,现在要设置 soap.wsdl_cache_enabled=0,否则,在开发代码时,您会遇到一些莫名其妙的行为。完成开发之后,要记得打开 WSDL 缓存,使代码运行得更快。

  为了便于参考,我们将在两种环境中使用 ext/soap:

Linux Centos 3.3(Red Hat EL 3 的免费重建版本)、Apache 2.0.47、PHP 5.0.2,需要升级 libxml2 到 2.6.12。
Windows XP SP1、Apache 2.0.46、PHP 5.0.2 二进制压缩包、libxml2 2.6.11。

  这些说明同样适用于其他配置。

  Weather Forecast 应用程序

  我们要从 PHP 中访问的 Web 服务是一个天气预报应用程序。这是 WebSphere Version 5.1 Application Developer 5.1.1 Web Services Handbook 中开发的示例应用程序。下载示例 Weather Forecast 应用程序,请参阅本文后面的下载部分。这本书设计了几种不同的场景,但我们只考虑一种,在该书中,这种场景称为“自下而上的开发,使用 HTTP 传输和 SOAP 消息从会话 EJB 生成 Web 服务”。在这里,自下而上的意思是说,Web 服务是围绕现有企业应用程序进行包装的。

图 1. 天气预报应用程序

  图 1 中标出的 Weather Forecast 应用程序的主要组成包括:

  预测天气的后台 WEATHER 数据库。天气预报中的信息包括:

   风向,八个方位
   风速,公里/小时
   气温,摄氏度
   天气状况:晴、有时阴、阴、雨、暴雨
   日期

  WeatherPredictor 类用于访问 WEATHER 数据库。如果数据库中没有适用于请求日期的预报,那么 WeatherPredictor 会随机生成天气预测(与实际的天气预报不同),并将它保存到数据库中。

  业务逻辑由 WeatherForecastEJB 会话 bean 提供,并公开为 Web 服务,它提供三项操作:

  getDayForecast 返回某一天的天气预报。

  getForecast 返回某个时期的天气预报。

  getTemperatures 返回某个时期的气温预测。

  将这个会话 bean 部署为 Web 服务所需的所有元素都是由 WebSphere Studio Application Developer 的 Web 服务向导生成的,并且是作为 ItsoWebService2RouterWeb 项目生成的。路由器 servlet 是连接 SOAP 消息和 EJB 容器的桥梁,需要配置和部署路由器 servlet,通过 URL ItsoWebService2EJBRouterWeb/services/WeatherServiceEJB 来使 Weather 服务可用。WSDL 文档 itso.session.WeatherForecastEJB.wsdl 在 ItsoWebService2EJBRouterWeb/wsdl 目录中。

  Java 客户机是这本书中开发的多个 Weather Service 客户机之一。ItsoWebService2EJBClient 项目中的 WeatherClientEJB 是一个简单的 Java servlet,调用 getForecast Web 服务操作。典型的运行结果如下所示:


图 2. Java WeatherClient


  下一步是在 PHP 中建立等价的客户机功能。

  阅读本文不需要自己运行这个例子,可以针对从 XMethods 网站选择的服务建立 PHP 客户机。

  PHP Weather 客户机

  这一节将建立我们自己的 PHP Weather 客户机。这里提供了一些代码片段,建议下载完整的客户机和 WSDL 文件。

  用于表示 Weather Service 的 ext/soap 类是 SoapClient。正如我们介绍 Weather Forecast 应用程序时所讨论的,我们知道应用服务器在 http://host:port/ItsoWebServer2RouterWeb/wsdl/itso/session/WeatherForecastEJB.wsdl 中提供了 WSDL。我们使用的是默认端口,并且在作为服务器的计算机上工作,这样就可以通过查找 WSDL 文件创建第一个 SoapClient:

<?php
$soapClient = new SoapClient("http://localhost:9080/" .
"ItsoWebService2RouterWeb/wsdl/itso/session/WeatherForecastEJB.wsdl");
?>


  注意,因为 ext/soap 是内置的,所以,在引用 SoapClient 之前,不需要任何 include 或 require 语句。

  现在已经实例化了客户机,还要联系 Weather 服务,并调用它的 getForecast 操作。在 WSDL 模式下使用 SoapClient 时,ext/soap 有一种很好的特性,即可以直接引用远程操作,就像它是 SoapClient 自身的函数一样。但是在建立输入参数时需要一点技巧。ext/soap 可以提供从 WSDL 中发现的操作和参数的数组:

$functions = $soapClient->__getFunctions();
print_r($functions);
$types = $soapClient->__getTypes();
print_r($types);


  只需要显示与 getForecast 相关的结果,并重新格式化这些结果,以方便阅读,于是我们看到以下代码:

getForecastResponse getForecast(getForecast $parameters)

struct getForecast {
dateTime startDate;
int days;
}

struct getForecastResponse {
Weather getForecastReturn;
}

struct Weather {
string condition;
dateTime date;
string windDirection;
int windSpeed;
int temperatureCelsius;
boolean dbflag;
}


  ext/soap 实际上并没有为我们定义 getForecast 类,我们必须创建该操作所需要的输入参数数组:

$getForecastParam = array('startDate' =>time(), 'days' => 3);


  然后像 SoapClient 的方法那样调用该操作:

$forecastResponse = $soapClient->getForecast($getForecastParam);


  最后我们得到了返回的 getForecastResponse 对象,它本身是一个 Weather 对象数组,然后在表格中显示结果:

echo "<table border=0 cellpadding=5>";
echo "<tr><th>Date</th><th>Condition</th><th>Temperature</th><th>Wind</th></tr>";
$weatherArray = $forecastResponse->getForecastReturn;
foreach ($weatherArray as $weather) {
echo "<tr>",
"<td>",strftime("%a. %b %d, %Y", strtotime($weather->date)),"</td>",
"<td>$weather->condition</td>",
"<td>$weather->temperatureCelsius</td>",
"<td>$weather->windDirection $weather->windSpeed</td>",
"</tr>";
}
echo "</table>";


  PHP 客户机与 Java 客户机的输出相同,于是我们知道圣诞节期间 San Jose 不会下雪……

图 3. PHP WeatherClient


  观察 SOAP 流

  我们成功地与 Weather 服务取得了联系,并显示了结果。但是如果出现错误,得不到预期的结果,该怎么办?ext/soap 可以显示客户机与服务器之间交换的 SOAP 消息,能够帮助我们确定问题所在。

  只有使用 trace 选项创建 SoapClient 时,才要使用跟踪功能。我们在 options 数组参数中设置 trace 选项,将该参数传递给 SoapClient 构造函数。我们将构造函数的使用改为:

$soapClient = new SoapClient("http://localhost:9080/" .
"ItsoWebService2RouterWeb/wsdl/itso/session/WeatherForecastEJB.wsdl",
array('trace' => 1));


  并在调用 goForecast 之后调用 trace 方法:

echo "Request :<br>", htmlspecialchars($soapClient->__getLastRequest()), "<br>";
echo "Response :<br>", htmlspecialchars($soapClient->__getLastResponse()), "<br>";


  一定要使用 htmlspecialchars 内置函数对 trace 输出进行编码,因为它将 SOAP xml 分界符转换成特殊字符,如 <,这样可以避免浏览器将其解释成标记。

  下面是某个请求的 trace 输出:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://session.itso">
<SOAP-ENV:Body>
<ns1:getForecast>
<ns1:startDate>2004-11-30T13:41:59</ns1:startDate>
<ns1:days>0</ns1:days>
</ns1:getForecast>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>


  对应的应答是:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<getForecastResponse xmlns="http://session.itso">
<getForecastReturn xmlns:ns-239399687="http://mapping.itso">
<ns-239399687:condition>sunny</ns-239399687:condition>
<ns-239399687:date>2004-11-30T00:00:00.000Z</ns-239399687:date>
<ns-239399687:windDirection>W</ns-239399687:windDirection>
<ns-239399687:windSpeed>18</ns-239399687:windSpeed>
<ns-239399687:temperatureCelsius>6</ns-239399687:temperatureCelsius>
<ns-239399687:dbflag>1</ns-239399687:dbflag>
</getForecastReturn>
</getForecastResponse>
</soapenv:Body>
</soapenv:Envelope>


  如果在开启跟踪功能的情况下运行客户机来收集这些输出,那么需要将 days 参数设置为 0,只有这样做,SOAP 应答才会输出较少的行。但是我们遇到了没有预料到的行为。我们本来期望 getForecastResponse 和以前一样是一个 Weather 对象数组,但是它应该只有一个元素,而不是 4 个元素。然而,它被转换成了一个简单的 Weather 对象,我们必须根据这种行为进行编码,就像您在最终的示例 PHP 客户机代码中看到的那样。这与 Java 客户机的行为有所不同,在客户机行为中,getForecast 总是返回 Weather 对象数组,无论服务器响应中有多少个 Weather 对象。SoapClient::_getTypes() 输出并没有为我们理解这种差异提供足够的细节,因此我们要求助于 WSDL 文档来了解完整的接口规范。

  解释 WSDL

  我们已经成功地调用了 Weather 服务,但是还没有看过它的 WSDL 文档。WSDL 中的细节要比 SoapClient 公开的多。我们如何知道应该在 startDate 参数中放什么呢?我们应该期望从返回的数据中实际得到什么?要回答这些问题,必须更深入地分析 WSDL。

  可以从下载部分下载 Weather Forecast 应用程序的 WSDL。如果使用不同的 Web 服务,只需要在浏览器中打开相应的 WSDL 文档即可。

  getForecast 操作的 WSDL 是:

<wsdl:operation name="getForecast">
<wsdl:input message="intf:getForecastRequest" name="getForecastRequest"/>
<wsdl:output message="intf:getForecastResponse" name="getForecastResponse"/>
</wsdl:operation>


  其中的 getForecastRequest 消息被定义为:

<wsdl:message name="getForecastRequest">
<wsdl:part element="intf:getForecast" name="parameters"/>
</wsdl:message>


  而 getForecast 结构被定义为:

<element name="getForecast">
<complexType>
<sequence>
<element name="startDate" nillable="true" type="xsd:dateTime"/>
<element name="days" type="xsd:int"/>
</sequence>
</complexType>
</element>


  于是我们知道该函数需要两个参数,xsd:dateTime 类型的 startDate 和整数类型的 days。这与我们所了解的 SoapClient::_getTypes 函数完全匹配,但是现在我们还知道 startDate 可以为空(nillable)。毫无疑问,如果我们简化输入参数,那么该函数将如下所示:

$forecastResponse = $soapClient->getForecast(array('startDate'=>Null, 'days'=>3));

  如果明确指定今天的日期,结果会与所指定的完全一致。

  如果希望制定其他起始日期怎么办呢?XML Schema将 dateTime 定义成一种基本类型,按照 ISO 8601 标准格式化,比如“2004-12-01T00:00:00”。假设希望了解三天之后的天气预报,可以使用内置函数 strtotime("+3 days") 获得需要的日期,该函数与 time() 函数相同,都返回标准 UNIX 格式的日期时间,即表示从公元纪年开始到现在的秒数的一个整数。我们知道 XML Schema 要求日期采用具有字符串字段的 ISO 8601 格式进行编码,于是在示例客户机中编写了 timeToIso8601 函数,将整数日期转换成 SOAP 编码定义的格式。但我们吃惊地发现,其实并不需要这样做,ext/soap 非常聪明地将整数日期转化成了需要的字符串字段格式。无论传递的是整数还是预格式化的字符串,都没有关系,最终传送的 SOAP 消息都是一样的。

  响应中的日期又如何呢?在回程中,ext/soap 从 SOAP 响应获得了 dateTime 字段,但是没有做任何格式转换。我们希望它返回一个整数,以表示从公元纪年到现在的秒数,但实际上得到的是按照 ISO 8601 格式化的字符串。于是我们使用 strtotime 函数将其转化成整数,然后使用 strftime 格式化该整数,以便于表示。

  Weather Service 按日期提供预报,但它忽略了 dateTime 编码中的时间成分。所以我们没有考虑这方面的调整,如果从运行在不同时区内的服务中请求天气预报,那么可能必须这样做。如果希望进一步了解时区转换,请参阅参考资料中给出的描述 ISO 8601 标准的文章。

  现在再回到响应格式上来。上一节中曾经提到 getForecast 返回数据的不一致性。WSDL 描述告诉我们 getForecast 返回一个 getForecastResponse 对象,getForecastResponse 可以包含无限多个称为 Weather 的复杂类型的列表:

<element name="getForecastResponse">
<complexType>
<sequence>
<element maxOccurs="unbounded" name="getForecastReturn" type="tns2:Weather"/>
</sequence>
</complexType>
</element>

<complexType name="Weather">
<sequence>
<element name="condition" nillable="true" type="xsd:string"/>
<element name="date" nillable="true" type="xsd:dateTime"/>
<element name="windDirection" nillable="true" type="xsd:string"/>
<element name="windSpeed" type="xsd:int"/>
<element name="temperatureCelsius" type="xsd:int"/>
<element name="dbflag" type="xsd:boolean"/>
</sequence>
</complexType>


  WSDL 不允许出现单元素数组这种特例。不幸的是,当响应只包含一个 Weather 对象时,ext/soap 没有考虑 WSDL 中应用于 getForecastResponse 的 <sequence> 标签,因为这种行为在客户机代码中造成了不必要的复杂性。

  最后,WSDL 文档还告诉 SOAP 客户机可以从网络中的哪个地方找到该服务:

<wsdl:service name="WeatherForecastEJBService">
<wsdl:port binding="intf:WeatherForecastEJBSoapBinding"
name="WeatherForecastEJB">
<wsdlsoap:address location=
"http://localhost:9080/ItsoWebService2RouterWeb/services/WeatherForecastEJB"/>
</wsdl:port>
</wsdl:service>

  处理 SOAP 错误

  如果运行客户机时出现错误怎么办?与其他语言(如 Java)一样,PHP 5 新增加了一种异常机制。ext/soap 使用这种新的机制,以 SoapFault 对象的形式返回错误。比方说,可以用下面这种形式将代码包装起来:

try {
... some SOAP operation
} catch (SoapFault $soapFault) {
echo $soapFault;
}

  注意,与 Java 有所不同,PHP 语言的 try - catch 块不能包含 finally 子句。

  SoapFault 可以在本地生成。比方说,假设输错了 getForecast 的 startDate 参数。客户机的输出就会变成:

SoapFault exception: [SOAP-ENV:Client]
SOAP-ERROR: Encoding: object hasn't 'startDate' property in WeatherClientEJB.php:32
Stack trace: #0 WeatherClientEJB.php(32): SoapClient->getForecast('getForecast', Array)
#1 WeatherClientEJB.php(73): displayForecast(Array)
#2 {main}

  注意,其中没有 trace 输出,因为并没有发送请求。SOAP_ENV:Client 是 SOAP 规范中为 Faulty body 元素 Faultcode 字段定义的值之一。

  这个 SoapFault 是在 ext/soap 内部发现错误时生成的,它没有发送 SOAP 消息。但是 SoapFaults 也可以报告服务器上发现的错误。比如,假设修改代码,将 startDate 参数的值设成“badDateString”。这是一个非法的 ISO 8601 字符串,但是 ext/soap 没有检查提供的格式,仅仅把消息发送到服务器,而服务器拒绝该请求:

Request :
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://session.itso">
<SOAP-ENV:Body>
<ns1:getForecast>
<ns1:startDate>badDateString</ns1:startDate>
<ns1:days>2</ns1:days>
</ns1:getForecast>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Response :
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<Fault xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<faultcode xmlns="">Server.generalException</faultcode>
<faultstring xmlns="">
<![CDATA[java.lang.NumberFormatException:
WSWS3046E: Error: Invalid date/time: badDateString]]>
</faultstring>
<detail xmlns=""/>
</Fault>
</soapenv:Body>
</soapenv:Envelope>

SoapFault exception: [Server.generalException] java.lang.NumberFormatException:
WSWS3046E: Error: Invalid date/time: badDateString in WeatherClientEJB.php:32
Stack trace: #0 WeatherClientEJB.php(32): SoapClient->getForecast('getForecast', Array)
#1 WeatherClientEJB.php(73): displayForecast(Array)
#2 {main}

  这一次,SOAP 请求传递给了服务器,但是因为日期格式无效而被拒绝。WeatherForecastEJB 实现抛出一个 java.lang.NumberFormatException,该异常在 SOAP 应答中作为 Faulty body 元素返回,然后作为一个 SoapFault 异常报告给客户机。

  保护 Web 服务

  我们考察了三种安全方法,以及如何在 PHP 中使用它们:

  基本 HTTP 身份验证

  如果 HTTP 服务器要求客户机进行身份验证,就会请求用户输入 id 和口令并在应答中增加 Authentication Required HTTP 头文件。在进行后续操作之前,客户机必须响应包含可接受 Authorization HTTP 头文件的请求。

  请求 HTTP 身份验证的通常是 Web 服务器,而不是 Web 服务提供者。Authentication Required HTTP 头文件被传递给浏览器,浏览器弹出对话框请求用户 id 和口令,然后将用户的应答作为 HTTP Authorization 头文件发送给 Web 服务器。在 PHP 脚本中很容易实现这一点,可以使用 header() 函数发送需要的 HTTP 头文件字段。例如:

if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="Weather"');
header("HTTP/1.0 401 Unauthorized");
}
echo "Welcome " . $_SERVER['PHP_AUTH_USER'];

  PHP 手册中的使用 PHP 进行 HTTP 身份验证 一章详细介绍了这个过程。

  您可能遇到这样一些 Web 服务,这些服务的提供者要求 PHP Web 服务客户机使用 HTTP 进行验证身份。ext/soap 提供了一种简单的发送 HTTP Authorization 请求头文件的方法,使用传递给 SoapClient 构造函数的 options 数组:

$soapClient = new SoapClient("http://localhost:9080/" .
"ItsoWebService2RouterWeb/wsdl/itso/session/WeatherForecastEJB.wsdl",
array('login' => "userid",
'password' => "password"));

ただし、ユーザー名とパスワードはネットワーク上でクリア テキストで渡されるため、HTTP 基本認証は (SSL などの他の外部セキュリティ システムと組み合わせて使用​​しない限り) 安全なユーザー認証方法とは見なされません。 HTTP ダイジェスト認証では、パスワードを暗号化することでこの方法が改善されていますが、すべてのブラウザがこの改善をサポートしているわけではありません。さらに、PHP の header() 関数は Basic 認証のみをサポートします。

SSL (Secure Sockets Layer)

より安全なプロトコルは HTTPS (HTTP over SSL) で、SSL を使用して HTTP メッセージを暗号化します。 SSL はトランスポート層で動作し、HTTP プロトコルや SOAP プロトコルについての知識はありません。したがって、メッセージの機密コンポーネントを暗号化するだけではなく、メッセージ全体を暗号化する必要があります。 HTTPS は、ブラウザと Web サーバーの間、または Web サーバーと Web サービス プロバイダーの間で使用できます。

OpenSSL がコンパイルされて有効になっている場合、PHP は HTTPS もサポートできます。 PHP スクリプトで SSL を使用する方法については、PHP マニュアルの OpenSSL の章を参照してください。

本人確認についてはどうですか? SSL は、相手が受け入れるか拒否できるセキュリティ証明書を送信できます。このアプローチは、クライアントが電子商取引アプリケーションなどの Web サービス プロバイダーに対して認証する必要がある場合に効果的です。ただし、Web サービス自体が機密情報へのアクセスを提供する場合でも、Web サービス プロバイダーは各顧客を認証する必要があります。証明書ベースの認証は、クライアントが多数で動的である可能性があり、事前に各クライアントに適切な証明書を配布することが非現実的であるため、適切ではありません。

WS-Security

WS-Security 標準は、Web サービスのセキュリティのためのさまざまな方法を提供します。これまでに検討してきたセキュリティ制御は、SOAP プロトコルの外側にあります。ただし、WS-Security は、SOAP メッセージにセキュリティ ヘッダー ファイルを追加することでセキュリティ制御を実装します。たとえば、(HTTP 基本認証とは異なり) WS-Security 基本認証の場合、次のタグが SOAP ヘッダー ファイルに表示されます:

<wsse:UsernameToken>
<wsse:Username>userid</wsse:Username>
<wsse:Password>password</wsse:Password>
</wsse:UsernameToken>
userid
password
:UsernameToken >

これは単なる例ですが、セキュリティ拡張機能の完全なセットは、認証だけでなく整合性や機密性なども含めて非常に充実しています。

現在、ext/soap には WS-Security に対する適切なサポートがありません。したがって、PHP で WS-Security ヘッダーを送受信する場合は、下位レベルのインターフェイスにドリルダウンして、SOAP ヘッダーを明示的に作成する必要があります。これまでの例では、ext/soap WSDL スキーマを使用してきました。ただし、SOAP メッセージ全体を制御するために使用できる非 WSDL パターンがあります。もちろん、コードでも多くの作業を行う必要があります。 SoapHeader、SoapParam、および SoapVar クラスを使用してメッセージを作成し、SoapClient::__call を使用して SOAP 要求を送信し、応答を受信できます。組み込みのサポートがなければ、PHP で Web サービスのセキュリティ拡張機能 (または WS-Transactions などのその他の高レベルの仕様) を記述するのはかなり難しい作業になるため、この記事ではそれを試みません。
結論


PHP SOAP 拡張機能を使用することは難しくありません。サーバーの実装方法に関係なく、わずか数行のコードで単純な Web サービスにアクセスする PHP スクリプトを開発できます。いつものように、使いやすさという点では PHP が優れています。この記事では主に、SoapClient クラスを使用して異種ネットワーク上の既存の Web サービスにアクセスする方法について説明しますが、ext/soap を使用すると、SoapServer クラスを使用して Web サービスをデプロイすることもでき、これも非常に直感的です。

より複雑な対話を処理したい場合、現在のバージョンの ext/soap ではあまり役に立ちません。 XML スキーマから PHP へのマッピングは不明瞭な場合があり、実験またはソース コードの研究によってのみ検証できます。より高度な Web サービス プロトコルを使用したい場合、唯一の選択肢は、非 WSDL スキーマを詳しく調べ、独自のスクリプトで SOAP ヘッダーを作成することですが、これは面倒でエラーが発生しやすくなります。 Web サービスの重要な命題は、さまざまなプラットフォーム、オペレーティング システム、プログラミング言語の相互運用性です。独立した WS-I (Web Services Interoperability) 組織は、基本プロファイルへの準拠を検証するためのテスト パッケージを提供しており、ext/soap が適応できることを示すある程度のレベルに到達することを期待しています。また、ext/soap が開発を続け、PHP の主流の拡張機能になることを期待しています。



声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。