[ZF-10089] Zend_Log
[zend.git] / documentation / manual / ja / module_specs / Zend_OpenId-Consumer.xml
blob544414d0afcda9c5a9911066ac607f02fe34b85e
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!-- Reviewed: no -->
3 <!-- EN-Revision: 20827 -->
4 <sect1 id="zend.openid.consumer">
5     <title>Zend_OpenId_Consumer の基本</title>
6     <para>
7         <classname>Zend_OpenId_Consumer</classname> を使用して、
8         ウェブサイト上の OpenID 認証スキーマを実装します。
9     </para>
11     <sect2 id="zend.openid.consumer.authentication">
12         <title>OpenID Authentication</title>
13         <para>
14             サイト開発者の視点で見ると、OpenID
15             の認証手続きは次の三段階となります。
16         </para>
18         <orderedlist>
19             <listitem>
20                 <para>
21                     OpenID 認証フォームを表示する。
22                 </para>
23             </listitem>
25             <listitem>
26                 <para>
27                     OpenID の識別子を受け取り、それを OpenID プロバイダに渡す。
28                 </para>
29             </listitem>
31             <listitem>
32                 <para>
33                     OpenID プロバイダからの応答を検証する。
34                 </para>
35             </listitem>
36         </orderedlist>
38         <para>
39             実際のところ、OpenID 認証プロトコルはもう少し複雑な手順を踏んでいます。
40             しかしその大半は <classname>Zend_OpenId_Consumer</classname>
41             の中にカプセル化されており、開発者側が意識する必要はありません。
42         </para>
44         <para>
45             OpenID 認証手続きはエンドユーザ側から始まるもので、
46             まず認証情報を適切な形式で入力してそれを送信するところから始まります。
47             次の例は、OpenID 識別子を受け付けるシンプルなフォームを表示するものです。
48             このサンプルはログイン画面を表示するだけのものであることに注意しましょう。
49         </para>
51         <example id="zend.openid.consumer.example-1">
52             <title>シンプルな OpenID ログインフォーム</title>
53             <programlisting language="php"><![CDATA[
54 <html><body>
55 <form method="post" action="example-1_2.php"><fieldset>
56 <legend>OpenID ログイン</legend>
57 <input type="text" name="openid_identifier">
58 <input type="submit" name="openid_action" value="login">
59 </fieldset></form></body></html>
60 ]]></programlisting>
61         </example>
63         <para>
64             このフォームを送信すると、OpenID 識別子が継ぎの <acronym>PHP</acronym>
65             スクリプトに渡されます。このスクリプトが、
66             認証の第二段階を処理します。
67             この <acronym>PHP</acronym> スクリプトで必要なのは、
68             <methodname>Zend_OpenId_Consumer::login()</methodname>
69             メソッドをコールすることだけです。
70             このメソッドの最初の引数は OpenID 識別子で、
71             2 番目の引数はスクリプトの <acronym>URL</acronym> となります。
72             ここで指定したスクリプトが認証の第三段階を処理します。
73         </para>
75         <example id="zend.openid.consumer.example-1_2">
76             <title>認証リクエストのハンドラ</title>
77             <programlisting language="php"><![CDATA[
78 $consumer = new Zend_OpenId_Consumer();
79 if (!$consumer->login($_POST['openid_identifier'], 'example-1_3.php')) {
80     die("OpenID でのログインに失敗しました。");
82 ]]></programlisting>
83         </example>
85         <para>
86             <methodname>Zend_OpenId_Consumer::login()</methodname>
87             は指定された識別子を調べ、成功した場合には識別プロバイダのアドレスと
88             そのローカル識別子を取得します。そして、
89             そのプロバイダとの関連付けを行い、
90             サイトとプロバイダが同じ秘密情報を共有するようにします。
91             この情報を使用してそれ以降のメッセージの署名を行います。
92             それから、認証リクエストをプロバイダに渡します。
93             このリクエストは、エンドユーザ側のウェブブラウザから
94             OpenID サーバサイトにリダイレクトされることに注意しましょう。
95             ユーザは、その後も認証手続きを進めることができます。
96         </para>
98         <para>
99             OpenID サーバがユーザに通常たずねるのは、
100             パスワード (ユーザがまだログインしていない場合) や
101             ユーザがこのサイトを信頼しているかどうか、
102             そしてそのサイトからどんな情報を返すかといった内容です。
103             これらのやりとりは、OpenID 対応のサイトからは見えない状態になるので、
104             ユーザのパスワードやその他の情報はオープンにはなりません。
105         </para>
107         <para>
108             成功した場合は <methodname>Zend_OpenId_Consumer::login()</methodname>
109             は何も返さずに <acronym>HTTP</acronym> リダイレクトを行います。
110             エラーが発生した場合は <constant>FALSE</constant> を返します。
111             エラーが発生するのは、たとえば識別子が無効だったり
112             プロバイダが死んでいたり、通信障害が発生したりした場合などです。
113         </para>
115         <para>
116             認証の第三段階の処理は、ユーザのパスワードによる認証を終えた
117             OpenID プロバイダからの応答によって始まります。
118             この応答は、ウェブブラウザの <acronym>HTTP</acronym> リダイレクトによって間接的に渡されます。
119             サイト側では、この応答が正しいものであるかどうかだけを確認することになります。
120         </para>
122         <example id="zend.openid.consumer.example-1_3">
123             <title>認証の応答の検証</title>
124             <programlisting language="php"><![CDATA[
125 $consumer = new Zend_OpenId_Consumer();
126 if ($consumer->verify($_GET, $id)) {
127     echo "有効 " . htmlspecialchars($id);
128 } else {
129     echo "無効 " . htmlspecialchars($id);
131 ]]></programlisting>
132         </example>
134         <para>
135             この検証は <classname>Zend_OpenId_Consumer::verify</classname>
136             メソッドで行います。このメソッドは、
137             <acronym>HTTP</acronym> リクエストの引数の配列全体を受け取って、
138             そのレスポンスが適切な OpenID プロバイダによって署名されたものかどうかを調べます。
139             また、エンドユーザが最初に入力した
140             OpenID 識別子を 2 番目の (オプションの) 引数として渡します。
141         </para>
142     </sect2>
144     <sect2 id="zend.openid.consumer.combine">
145         <title>すべての処理をひとつのページにまとめる</title>
146         <para>
147             次の例は、これらの三段階をひとつにまとめたものです。
148             それ以外に特別な付加機能はありません。
149             唯一の利点は、次の段階を処理するスクリプトの
150             <acronym>URL</acronym> を指定しなくてもよくなるということです。
151             デフォルトでは、すべての段階を同じ <acronym>URL</acronym> で処理します。
152             ただ、このスクリプトの内部にはディスパッチ用のコードが含まれており、
153             認証の各段階に応じて適切なコードに処理を振り分けるようになっています。
154         </para>
156         <example id="zend.openid.consumer.example-2">
157             <title>完全な OpenID ログインスクリプト</title>
158             <programlisting language="php"><![CDATA[
159 <?php
160 $status = "";
161 if (isset($_POST['openid_action']) &&
162     $_POST['openid_action'] == "login" &&
163     !empty($_POST['openid_identifier'])) {
165     $consumer = new Zend_OpenId_Consumer();
166     if (!$consumer->login($_POST['openid_identifier'])) {
167         $status = "OpenID でのログインに失敗しました。";
168     }
169 } else if (isset($_GET['openid_mode'])) {
170     if ($_GET['openid_mode'] == "id_res") {
171         $consumer = new Zend_OpenId_Consumer();
172         if ($consumer->verify($_GET, $id)) {
173             $status = "有効 " . htmlspecialchars($id);
174         } else {
175             $status = "無効 " . htmlspecialchars($id);
176         }
177     } else if ($_GET['openid_mode'] == "cancel") {
178         $status = "キャンセル";
179     }
182 <html><body>
183 <?php echo "$status<br>" ?>
184 <form method="post">
185 <fieldset>
186 <legend>OpenID ログイン</legend>
187 <input type="text" name="openid_identifier" value=""/>
188 <input type="submit" name="openid_action" value="login"/>
189 </fieldset>
190 </form>
191 </body></html>
192 ]]></programlisting>
193         </example>
195         <para>
196             さらに、このコードでは
197             キャンセルされた場合と認証の応答が間違っていた場合を区別しています。
198             プロバイダの応答がキャンセルとなるのは、
199             識別プロバイダがその識別子について知らなかった場合や
200             ユーザがログインしていない場合、
201             あるいはユーザがそのサイトを信頼しない場合などです。
202             応答が間違っているのは、
203             署名が間違っている場合などです。
204         </para>
205     </sect2>
207     <sect2 id="zend.openid.consumer.realm">
208         <title>コンシューマレルム</title>
209         <para>
210             OpenID 対応のサイトがプロバイダへの認証リクエストを通過すると、
211             自分自身をレルム <acronym>URL</acronym> で識別するようになります。
212             この <acronym>URL</acronym> は、信頼済みサイトのルートとみなされます。
213             ユーザがその <acronym>URL</acronym> を信頼すると、
214             その配下の <acronym>URL</acronym> も同様に信頼することになります。
215         </para>
217         <para>
218             デフォルトでは、レルム <acronym>URL</acronym> は自動的にログインスクリプトがあるディレクトリの
219             <acronym>URL</acronym> に設定されます。大半の場合はこれで大丈夫ですが、
220             そうではない場合もあります。
221             際と全体で共通のログインスクリプトを使用している場合や、
222             ひとつのドメインで複数のサーバを組み合わせて使用している場合などです。
223         </para>
225         <para>
226             このような場合は、レルムの値を
227             <classname>Zend_OpenId_Consumer::login</classname> メソッドの 3 番目の引数として渡すことができます。
228             次の例は、すべての php.net サイトへの信頼済みアクセスを一度に確認するものです。
229         </para>
231         <example id="zend.openid.consumer.example-3_2">
232             <title>指定したレルムへの認証リクエスト</title>
233             <programlisting language="php"><![CDATA[
234 $consumer = new Zend_OpenId_Consumer();
235 if (!$consumer->login($_POST['openid_identifier'],
236                       'example-3_3.php',
237                       'http://*.php.net/')) {
238     die("OpenID でのログインに失敗しました。");
240 ]]></programlisting>
241         </example>
243         <para>
244             以下の例では、認証の第二段階のみを実装しています。
245             それ以外の段階については最初の例と同じです。
246         </para>
247     </sect2>
249     <sect2 id="zend.openid.consumer.check">
250         <title>即時確認</title>
251         <para>
252             場合によっては、信頼済み OpenID
253             サーバにそのユーザがログインしているかどうかを
254             ユーザとのやりとりなしに知りたいこともあります。
255             そのような場合に最適なメソッドが <classname>Zend_OpenId_Consumer::check</classname>
256             です。このメソッドの引数は <classname>Zend_OpenId_Consumer::login</classname>
257             とまったく同じですが、ユーザ側には OpenID サーバのページを一切見せません。
258             したがって、ユーザ側から見れば処理は透過的に行われ、
259             まるで他のサイトに一切移動していないように見えるようになります。
260             そのユーザがすでにログインしており、かつそのサイトを信頼している場合に
261             第三段階の処理が成功し、それ以外の場合は失敗します。
262         </para>
264         <example id="zend.openid.consumer.example-4">
265             <title>対話形式でない即時確認</title>
266             <programlisting language="php"><![CDATA[
267 $consumer = new Zend_OpenId_Consumer();
268 if (!$consumer->check($_POST['openid_identifier'], 'example-4_3.php')) {
269     die("OpenID でのログインに失敗しました。");
271 ]]></programlisting>
272         </example>
274         <para>
275             以下の例では、認証の第二段階のみを実装しています。
276             それ以外の段階については最初の例と同じです。
277         </para>
278     </sect2>
280     <sect2 id="zend.openid.consumer.storage">
281         <title>Zend_OpenId_Consumer_Storage</title>
282         <para>
283             OpenID の認証手続きは三段階に分かれており、
284             それぞれで別々の <acronym>HTTP</acronym> リクエストを使用します。
285             それらのリクエスト間で情報を保存するため、
286             <classname>Zend_OpenId_Consumer</classname> では内部ストレージを使用します。
287         </para>
289         <para>
290             開発者は特にこのストレージを気にする必要はありません。
291             デフォルトで、<classname>Zend_OpenId_Consumer</classname>
292             は /tmp 配下のファイルベースのストレージを使用するからです。
293             これは <acronym>PHP</acronym> のセッションと同じ挙動です。
294             しかし、このストレージがあらゆる場合にうまく使えるというわけではありません。
295             たとえばその手の情報はデータベースに保存したいという人もいるでしょうし、
296             大規模なウェブファームで共通のストレージを使用したいこともあるでしょう。
297             幸いなことに、このデフォルトのストレージは簡単に変更できます。
298             そのために必要なのは、<classname>Zend_OpenId_Consumer_Storage</classname>
299             クラスを継承した独自のストレージクラスを実装して
300             それを <classname>Zend_OpenId_Consumer</classname>
301             のコンストラクタへの最初の引数として渡すことだけです。
302         </para>
304         <para>
305             次の例は、バックエンドとして <classname>Zend_Db</classname>
306             を使用するシンプルなストレージです。三種類の機能を持っています。
307             最初の機能は関連付けの情報、そして 2 番目が確認した内容のキャッシュ、
308             そして 3 番目が応答の一意性の確認です。このクラスは、
309             既存のデータベースや新しいデータベースで簡単に使用できるように実装されています。
310             必要に応じて、もしまだテーブルが存在しなければ自動的にテーブルを作成します。
311         </para>
313         <example id="zend.openid.consumer.example-5">
314             <title>データベースストレージ</title>
315             <programlisting language="php"><![CDATA[
316 class DbStorage extends Zend_OpenId_Consumer_Storage
318     private $_db;
319     private $_association_table;
320     private $_discovery_table;
321     private $_nonce_table;
323     // Zend_Db_Adapter オブジェクトと
324     // テーブル名を渡します
325     public function __construct($db,
326                                 $association_table = "association",
327                                 $discovery_table = "discovery",
328                                 $nonce_table = "nonce")
329     {
330         $this->_db = $db;
331         $this->_association_table = $association_table;
332         $this->_discovery_table = $discovery_table;
333         $this->_nonce_table = $nonce_table;
334         $tables = $this->_db->listTables();
336         // アソシエーションテーブルが存在しない場合は作成します
337         if (!in_array($association_table, $tables)) {
338             $this->_db->getConnection()->exec(
339                 "create table $association_table (" .
340                 " url     varchar(256) not null primary key," .
341                 " handle  varchar(256) not null," .
342                 " macFunc char(16) not null," .
343                 " secret  varchar(256) not null," .
344                 " expires timestamp" .
345                 ")");
346         }
348         // ディスカバリーテーブルが存在しない場合は作成します
349         if (!in_array($discovery_table, $tables)) {
350             $this->_db->getConnection()->exec(
351                 "create table $discovery_table (" .
352                 " id      varchar(256) not null primary key," .
353                 " realId  varchar(256) not null," .
354                 " server  varchar(256) not null," .
355                 " version float," .
356                 " expires timestamp" .
357                 ")");
358         }
360         // ノンステーブルが存在しない場合は作成します
361         if (!in_array($nonce_table, $tables)) {
362             $this->_db->getConnection()->exec(
363                 "create table $nonce_table (" .
364                 " nonce   varchar(256) not null primary key," .
365                 " created timestamp default current_timestamp" .
366                 ")");
367         }
368     }
370     public function addAssociation($url,
371                                    $handle,
372                                    $macFunc,
373                                    $secret,
374                                    $expires)
375     {
376         $table = $this->_association_table;
377         $secret = base64_encode($secret);
378         $this->_db->insert($table, array(
379             'url'     => $url,
380             'handle'  => $handle,
381             'macFunc' => $macFunc,
382             'secret'  => $secret,
383             'expires' => $expires,
384         ));
385         return true;
386     }
388     public function getAssociation($url,
389                                    &$handle,
390                                    &$macFunc,
391                                    &$secret,
392                                    &$expires)
393     {
394         $table = $this->_association_table;
395         $this->_db->delete(
396             $table, $this->_db->quoteInto('expires < ?', time())
397         );
398         $select = $this-_db->select()
399                 ->from($table, array('handle', 'macFunc', 'secret', 'expires'))
400                 ->where('url = ?', $url);
401         $res = $this->_db->fetchRow($select);
403         if (is_array($res)) {
404             $handle  = $res['handle'];
405             $macFunc = $res['macFunc'];
406             $secret  = base64_decode($res['secret']);
407             $expires = $res['expires'];
408             return true;
409         }
410         return false;
411     }
413     public function getAssociationByHandle($handle,
414                                            &$url,
415                                            &$macFunc,
416                                            &$secret,
417                                            &$expires)
418     {
419         $table = $this->_association_table;
420         $this->_db->delete(
421             $table, $this->_db->quoteInto('expires < ', time())
422         );
423         $select = $this->_db->select()
424                 ->from($table, array('url', 'macFunc', 'secret', 'expires')
425                 ->where('handle = ?', $handle);
426         $res = $select->fetchRow($select);
428         if (is_array($res)) {
429             $url     = $res['url'];
430             $macFunc = $res['macFunc'];
431             $secret  = base64_decode($res['secret']);
432             $expires = $res['expires'];
433             return true;
434         }
435         return false;
436     }
438     public function delAssociation($url)
439     {
440         $table = $this->_association_table;
441         $this->_db->query("delete from $table where url = '$url'");
442         return true;
443     }
445     public function addDiscoveryInfo($id,
446                                      $realId,
447                                      $server,
448                                      $version,
449                                      $expires)
450     {
451         $table = $this->_discovery_table;
452         $this->_db->insert($table, array(
453             'id'      => $id,
454             'realId'  => $realId,
455             'server'  => $server,
456             'version' => $version,
457             'expires' => $expires,
458         ));
460         return true;
461     }
463     public function getDiscoveryInfo($id,
464                                      &$realId,
465                                      &$server,
466                                      &$version,
467                                      &$expires)
468     {
469         $table = $this->_discovery_table;
470         $this->_db->delete($table, $this->quoteInto('expires < ?', time()));
471         $select = $this->_db->select()
472                 ->from($table, array('realId', 'server', 'version', 'expires'))
473                 ->where('id = ?', $id);
474         $res = $this->_db->fetchRow($select);
476         if (is_array($res)) {
477             $realId  = $res['realId'];
478             $server  = $res['server'];
479             $version = $res['version'];
480             $expires = $res['expires'];
481             return true;
482         }
483         return false;
484     }
486     public function delDiscoveryInfo($id)
487     {
488         $table = $this->_discovery_table;
489         $this->_db->delete($table, $this->_db->quoteInto('id = ?', $id));
490         return true;
491     }
493     public function isUniqueNonce($nonce)
494     {
495         $table = $this->_nonce_table;
496         try {
497             $ret = $this->_db->insert($table, array(
498                 'nonce' => $nonce,
499             ));
500         } catch (Zend_Db_Statement_Exception $e) {
501             return false;
502         }
503         return true;
504     }
506     public function purgeNonces($date=null)
507     {
508     }
511 $db = Zend_Db::factory('Pdo_Sqlite',
512     array('dbname'=>'/tmp/openid_consumer.db'));
513 $storage = new DbStorage($db);
514 $consumer = new Zend_OpenId_Consumer($storage);
515 ]]></programlisting>
516         </example>
518         <para>
519             このサンプルには OpenID の認証コードそのものは含まれません。
520             しかし、先ほどの例やこの後の例と同じロジックに基づいています。
521         </para>
522     </sect2>
524     <sect2 id="zend.openid.consumer.sreg">
525         <title>Simple Registration Extension</title>
526         <para>
527             認証に加えて、OpenID は軽量なプロファイル交換のためにも使用できます。
528             この機能は OpenID 認証の仕様ではカバーされておらず、
529             OpenID Simple Registration Extension プロトコルで対応しています。
530             このプロトコルを使用すると、
531             OpenID 対応のサイトがエンドユーザに関する情報を
532             OpenID プロバイダから取得できるようになります。
533             取得できる情報には次のようなものがあります。
534         </para>
536         <itemizedlist>
537             <listitem>
538                 <para>
539                     <emphasis>nickname</emphasis>
540                      - ユーザがニックネームとして使用している UTF-8 文字列。
541                 </para>
542             </listitem>
543             <listitem>
544                 <para>
545                     <emphasis>email</emphasis>
546                     - エンドユーザのメールアドレス。RFC2822 のセクション 3.4.1
547                     の形式。
548                 </para>
549             </listitem>
550             <listitem>
551                 <para>
552                     <emphasis>fullname</emphasis>
553                     - エンドユーザのフルネームを表す UTF-8 文字列。
554                 </para>
555             </listitem>
556             <listitem>
557                 <para>
558                     <emphasis>dob</emphasis>
559                     - エンドユーザの誕生日を YYYY-MM-DD 形式で表したもの。
560                     指定されている桁数より少ない場合は、ゼロ埋めされます。
561                     この値は常に 10 文字となります。
562                     エンドユーザがこの情報の公開を希望しない場合は、
563                     その部分の値をゼロに設定する必要があります。
564                     たとえば、1980 年生まれであることは公開するが
565                     月や日は公開したくないというエンドユーザの場合、
566                     返される値は "1980-00-00" となります。
567                 </para>
568             </listitem>
569             <listitem>
570                 <para>
571                     <emphasis>gender</emphasis>
572                     - エンドユーザの姓。"M" が男性で "F" が女性。
573                 </para>
574             </listitem>
575             <listitem>
576                 <para>
577                     <emphasis>postcode</emphasis>
578                     - エンドユーザの国の郵便システムに対応した UTF-8 文字列。
579                 </para>
580             </listitem>
581             <listitem>
582                 <para>
583                     <emphasis>country</emphasis>
584                     - エンドユーザの居住地 (国) を ISO3166 形式で表したもの。
585                 </para>
586             </listitem>
587             <listitem>
588                 <para>
589                     <emphasis>language</emphasis>
590                     - エンドユーザの使用言語を ISO639 形式で表したもの。
591                 </para>
592             </listitem>
593             <listitem>
594                 <para>
595                     <emphasis>timezone</emphasis>
596                     - TimeZone データベースの <acronym>ASCII</acronym> 文字列。
597                     "Europe/Paris" あるいは "America/Los_Angeles" など。
598                 </para>
599             </listitem>
600         </itemizedlist>
602         <para>
603             OpenID 対応のウェブサイトからは、
604             これらのフィールドの任意の組み合わせについて問い合わせられます。
605             また、いくつかの情報についてのみ厳密に問い合わせを行い、
606             それ以外の情報については開示するかしないかをユーザに決めさせることもできます。
607             次の例は、<emphasis>nickname</emphasis> およびオプションで
608             <emphasis>email</emphasis> と <emphasis>fullname</emphasis>
609             を要求する <classname>Zend_OpenId_Extension_Sreg</classname>
610             クラスのオブジェクトを作成するものです。
611         </para>
613         <example id="zend.openid.consumer.example-6_2">
614             <title>Simple Registration Extension のリクエストの送信</title>
615             <programlisting language="php"><![CDATA[
616 $sreg = new Zend_OpenId_Extension_Sreg(array(
617     'nickname'=>true,
618     'email'=>false,
619     'fullname'=>false), null, 1.1);
620 $consumer = new Zend_OpenId_Consumer();
621 if (!$consumer->login($_POST['openid_identifier'],
622                       'example-6_3.php',
623                       null,
624                       $sreg)) {
625     die("OpenID でのログインに失敗しました。");
627 ]]></programlisting>
628         </example>
630         <para>
631             見てのとおり、<classname>Zend_OpenId_Extension_Sreg</classname>
632             のコンストラクタに渡すのは問い合わせたいフィールドの配列です。
633             この配列のインデックスはフィールド名、値はフラグとなります。
634             <constant>TRUE</constant> はそのフィールドが必須であること、そして
635             <constant>FALSE</constant> はそのフィールドがオプションであることを表します。
636             <classname>Zend_OpenId_Consumer::login</classname> の 4 番目の引数には、
637             extension あるいは extension のリストを指定できます。
638         </para>
640         <para>
641             認証の第三段階で、<classname>Zend_OpenId_Extension_Sreg</classname>
642             オブジェクトが <classname>Zend_OpenId_Consumer::verify</classname>
643             に渡されます。そして、認証に成功すると、
644             <classname>Zend_OpenId_Extension_Sreg::getProperties</classname>
645             は要求されたフィールドの配列を返します。
646         </para>
648         <example id="zend.openid.consumer.example-6_3">
649             <title>Simple Registration Extension の応答内容の検証</title>
650             <programlisting language="php"><![CDATA[
651 $sreg = new Zend_OpenId_Extension_Sreg(array(
652     'nickname'=>true,
653     'email'=>false,
654     'fullname'=>false), null, 1.1);
655 $consumer = new Zend_OpenId_Consumer();
656 if ($consumer->verify($_GET, $id, $sreg)) {
657     echo "有効 " . htmlspecialchars($id) . "<br>\n";
658     $data = $sreg->getProperties();
659     if (isset($data['nickname'])) {
660         echo "nickname: " . htmlspecialchars($data['nickname']) . "<br>\n";
661     }
662     if (isset($data['email'])) {
663         echo "email: " . htmlspecialchars($data['email']) . "<br>\n";
664     }
665     if (isset($data['fullname'])) {
666         echo "fullname: " . htmlspecialchars($data['fullname']) . "<br>\n";
667     }
668 } else {
669     echo "無効 " . htmlspecialchars($id);
671 ]]></programlisting>
672         </example>
674         <para>
675             引数を渡さずに <classname>Zend_OpenId_Extension_Sreg</classname>
676             を作成した場合は、必要なデータが存在するかどうかを
677             ユーザ側のコードで調べなければなりません。
678             しかし、第二段階で必要となるフィールドと同じ内容のリストでオブジェクトを作成した場合は、
679             必要なデータの存在は自動的にチェックされます。
680             この場合、必須フィールドのいずれかが存在しなければ
681             <classname>Zend_OpenId_Consumer::verify</classname> は
682             <constant>FALSE</constant> を返します。
683         </para>
685         <para>
686             デフォルトでは <classname>Zend_OpenId_Extension_Sreg</classname> はバージョン
687             1.0 を使用します。バージョン 1.1 の仕様はまだ確定していないからです。
688             しかし、中にはバージョン 1.0 の機能では完全にはサポートしきれないライブラリもあります。
689             たとえば www.myopenid.com ではリクエストに SREG
690             名前空間が必須となりますが、これは 1.1 にしか存在しません。
691             このサーバを使用する場合は、<classname>Zend_OpenId_Extension_Sreg</classname>
692             のコンストラクタで明示的にバージョン 1.1 を指定する必要があります。
693         </para>
695         <para>
696             <classname>Zend_OpenId_Extension_Sreg</classname> のコンストラクタの 2 番目の引数は、
697             ポリシーの <acronym>URL</acronym> です。これは、識別プロバイダがエンドユーザに提供する必要があります。
698         </para>
699     </sect2>
701     <sect2 id="zend.openid.consumer.auth">
702         <title>Zend_Auth との統合</title>
703         <para>
704             Zend Framework には、ユーザ認証用のクラスが用意されています。
705             そう、<classname>Zend_Auth</classname> のことです。
706             このクラスを <classname>Zend_OpenId_Consumer</classname>
707             と組み合わせて使うこともできます。次の例は、
708             <code>OpenIdAdapter</code> が
709             <classname>Zend_Auth_Adapter_Interface</classname> の
710             <code>authenticate</code> メソッドを実装する方法を示すものです。
711             これは、認証問い合わせと検証を行います。
712         </para>
714         <para>
715             このアダプタと既存のアダプタの大きな違いは、
716             このアダプタが 2 回の <acronym>HTTP</acronym> リクエストで動作することと
717             OpenID 認証の第二段階、第三段階用に処理を振り分けるコードがあることです。
718         </para>
720         <example id="zend.openid.consumer.example-7">
721             <title>OpenID 用の Zend_Auth アダプタ</title>
722             <programlisting language="php"><![CDATA[
723 <?php
724 class OpenIdAdapter implements Zend_Auth_Adapter_Interface {
725     private $_id = null;
727     public function __construct($id = null) {
728         $this->_id = $id;
729     }
731     public function authenticate() {
732         $id = $this->_id;
733         if (!empty($id)) {
734             $consumer = new Zend_OpenId_Consumer();
735             if (!$consumer->login($id)) {
736                 $ret = false;
737                 $msg = "認証に失敗しました。";
738             }
739         } else {
740             $consumer = new Zend_OpenId_Consumer();
741             if ($consumer->verify($_GET, $id)) {
742                 $ret = true;
743                 $msg = "認証に成功しました。";
744             } else {
745                 $ret = false;
746                 $msg = "認証に失敗しました。";
747             }
748         }
749         return new Zend_Auth_Result($ret, $id, array($msg));
750     }
753 $status = "";
754 $auth = Zend_Auth::getInstance();
755 if ((isset($_POST['openid_action']) &&
756      $_POST['openid_action'] == "login" &&
757      !empty($_POST['openid_identifier'])) ||
758     isset($_GET['openid_mode'])) {
759     $adapter = new OpenIdAdapter(@$_POST['openid_identifier']);
760     $result = $auth->authenticate($adapter);
761     if ($result->isValid()) {
762         Zend_OpenId::redirect(Zend_OpenId::selfURL());
763     } else {
764         $auth->clearIdentity();
765         foreach ($result->getMessages() as $message) {
766             $status .= "$message<br>\n";
767         }
768     }
769 } else if ($auth->hasIdentity()) {
770     if (isset($_POST['openid_action']) &&
771         $_POST['openid_action'] == "logout") {
772         $auth->clearIdentity();
773     } else {
774         $status = $auth->getIdentity() . " としてログインしました。<br>\n";
775     }
778 <html><body>
779 <?php echo htmlspecialchars($status);?>
780 <form method="post"><fieldset>
781 <legend>OpenID ログイン</legend>
782 <input type="text" name="openid_identifier" value="">
783 <input type="submit" name="openid_action" value="login">
784 <input type="submit" name="openid_action" value="logout">
785 </fieldset></form></body></html>
786 ]]></programlisting>
787         </example>
789         <para>
790             <classname>Zend_Auth</classname> と組み合わせた場合、
791             エンドユーザの識別子はセッションに保存されます。
792             これを取得するには <classname>Zend_Auth::hasIdentity</classname>
793             および <classname>Zend_Auth::getIdentity</classname>
794             を使用します。
795         </para>
796     </sect2>
798     <sect2 id="zend.openid.consumer.mvc">
799         <title>Zend_Controller との統合</title>
800         <para>
801             最後に、Model-View-Controller
802             アプリケーションへの組み込みについて簡単に説明しておきます。
803             Zend Framework のアプリケーションは
804             <classname>Zend_Controller</classname> クラスを使用して実装されており、
805             エンドユーザのウェブブラウザに返す <acronym>HTTP</acronym> レスポンスは
806             <classname>Zend_Controller_Response_Http</classname>
807             クラスのオブジェクトを使用して準備しています。
808         </para>
810         <para>
811             <classname>Zend_OpenId_Consumer</classname> には GUI 機能はありませんが、
812             <classname>Zend_OpenId_Consumer::login</classname> および
813             <classname>Zend_OpenId_Consumer::check</classname>
814             に成功した場合に <acronym>HTTP</acronym> リダイレクトを行います。
815             もしそれ以前に何らかの情報がウェブブラウザに送信されていると、
816             このリダイレクトがうまく動作しません。
817             <acronym>MVC</acronym> コードで <acronym>HTTP</acronym> リダイレクトを正しく機能させるため、
818             <classname>Zend_OpenId_Consumer::login</classname> あるいは
819             <classname>Zend_OpenId_Consumer::check</classname> の最後の引数に
820             <classname>Zend_Controller_Response_Http</classname> を渡す必要があります。
821         </para>
822     </sect2>
823 </sect1>
824 <!--
825 vim:se ts=4 sw=4 et: