Zend_OpenId-Consumer.xml 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- EN-Revision: 15617 -->
  3. <!-- Reviewed: no -->
  4. <sect1 id="zend.openid.consumer">
  5. <title>Zend_OpenId_Consumer Grundlagen</title>
  6. <para>
  7. <classname>Zend_OpenId_Consumer</classname> kann verwendet werdeb um OpenID
  8. Authentifizierung auf Webseiten zu implementieren.
  9. </para>
  10. <sect2 id="zend.openid.consumer.authentication">
  11. <title>OpenID Authentifikation</title>
  12. <para>
  13. Aus der Sicht eines Website Entwicklers, geschieht die Authentifikation von OpenID in
  14. drei Schritten:
  15. </para>
  16. <orderedlist>
  17. <listitem>
  18. <para>
  19. Zeige das OpenID Authentifikations Formular
  20. </para>
  21. </listitem>
  22. <listitem>
  23. <para>
  24. Akzeptiere die OpenID Identität und übergib Sie an den OpenID Provider
  25. </para>
  26. </listitem>
  27. <listitem>
  28. <para>
  29. Überprüfe die Antwort des OpenID Providers
  30. </para>
  31. </listitem>
  32. </orderedlist>
  33. <para>
  34. Das OpenID Authentifikations Protokoll benötigt aktuell mehrere, aber viele von Ihnen
  35. sind innerhalb von <classname>Zend_OpenId_Consumer</classname> gekapselt, und deshalb
  36. für den Entwickler transparent.
  37. </para>
  38. <para>
  39. Der End-Benutzer initiiert den OpenID Authentifikations Prozess indem er Seine oder Ihre
  40. Identifikations Daten in der entsprechenden Form übermittelt. Das folgende Beispiel
  41. zeigt ein einfaches Formular das einen OpenID Identifikator akzeptiert. Es gilt zu
  42. beachten das das Beispiel nur einen Login demonstriert.
  43. </para>
  44. <example id="zend.openid.consumer.example-1">
  45. <title>Das einfache OpenID Login Formular</title>
  46. <programlisting language="php"><![CDATA[
  47. <html><body>
  48. <form method="post" action="example-1_2.php"><fieldset>
  49. <legend>OpenID Login</legend>
  50. <input type="text" name="openid_identifier">
  51. <input type="submit" name="openid_action" value="login">
  52. </fieldset></form></body></html>
  53. ]]></programlisting>
  54. </example>
  55. <para>
  56. Dieses Formular übergibt bei der Übertragung eine OpenID Identität an das folgende PHP
  57. Skript welches den zweiten Schritt der Authentifizierung durchführt. Das PHP Skript muss
  58. in diesem Schritt nur die <classname>Zend_OpenId_Consumer::login()</classname> Methode
  59. aufrufen. Das erste Argument dieser Methode akzeptiert eine OpenID Identität, und das
  60. zweite ist die URL des Skripts das den dritten und letzten Schritt der Authentifizierung
  61. behandelt.
  62. </para>
  63. <example id="zend.openid.consumer.example-1_2">
  64. <title>Der Authentifizierungs Anfrage Handler</title>
  65. <programlisting language="php"><![CDATA[
  66. $consumer = new Zend_OpenId_Consumer();
  67. if (!$consumer->login($_POST['openid_identifier'], 'example-1_3.php')) {
  68. die("OpenID Login fehlgeschlagen.");
  69. }
  70. ]]></programlisting>
  71. </example>
  72. <para>
  73. Die <classname>Zend_OpenId_Consumer::login()</classname> Methode führt eine Suche nach
  74. einem gegebenen Identifikator durch und findet, bei Erfolg, die Adresse des Identitäts
  75. Providers und dessen Lokalen Idenzifizierer durch. Dann erstellt es eine Assoziation zum
  76. gegebenen Provider sodas beide, die Site und der Provider, um das gleiche Geheimnis
  77. teilen das verwendet wird um nachfolgende Nachrichten zu verschlüsseln. Letztendlich
  78. wird eine Authentifikations Anfrage an den Provider übergeben. Diese Anfrage leitet den
  79. Web-Browser des End-Benutzers zu einer OpenID Server Site um, wo der Benutzer die
  80. Möglichkeit habt den Authentifizierungs Prozess fortzuführen.
  81. </para>
  82. <para>
  83. Ein OpenID Provider fragt nochmalerweise Benutzer nach Ihrem Passwort (wenn Sie vorher
  84. noch nicht angemeldet waren), wenn der Benutzer dieser Site vertraut und welche
  85. Informationen zu der Site zurückgegeben werden können. Diese Interaktionen sind für den
  86. OpenID Konsument nicht sichtbar sodas es für Ihn keine Möglichkeit gibt das
  87. Benutzerpasswort oder andere Informationen zu bekommen bei denen der Benutzer nicht
  88. gesagt hat das der OpenId Provider Sie teilen darf.
  89. </para>
  90. <para>
  91. Bei Erfolg wird <classname>Zend_OpenId_Consumer::login()</classname> nicht zurückkommen,
  92. sondern eine HTTP Umleitung durchführt. Trotzdem wird im Falle eine Fehler ein false
  93. zurückgeben wird. Fehler können durch eine ungültige Identität, einen Provider der nicht
  94. antwortet, Kommunikations Fehler, usw. auftreten.
  95. </para>
  96. <para>
  97. Der dritte Schritt der Authentifikation wird durch die Antwort vom OpenID Provider
  98. initiiert, nachdem dieser das Benutzerpasswort authentifiziert hat. Diese Antwort wird
  99. indirekt, als HTTP Umleitung übergeben, indem der Webbrowsers des End-Benutzers
  100. verwendet wird. Der Konsument muß nun einfach prüfen ob die Antwort gültig ist.
  101. </para>
  102. <example id="zend.openid.consumer.example-1_3">
  103. <title>Der Authentifizierungs Antwort Prüfer</title>
  104. <programlisting language="php"><![CDATA[
  105. $consumer = new Zend_OpenId_Consumer();
  106. if ($consumer->verify($_GET, $id)) {
  107. echo "GÜLTIG ". htmlspecialchars($id);
  108. } else {
  109. echo "UNGÜLTIG" . htmlspecialchars($id);
  110. }
  111. ]]></programlisting>
  112. </example>
  113. <para>
  114. Diese Prüfung wird durchgeführt indem die
  115. <classname>Zend_OpenId_Consumer::verify</classname> Methode verwendet wird, welche ein
  116. ganzes Array von HTTP Anfrage Argumenten entgegennimmt und prüft ob diese Antwort durch
  117. den OpenID Provider richtig signiert wurde. Sie kann die erhaltete OpenID Identität, die
  118. vom Endbenutzer im ersten Schritt angegeben wurde, zuordnen, indem ein zweites,
  119. optionales, Argument eingegeben wird.
  120. </para>
  121. </sect2>
  122. <sect2 id="zend.openid.consumer.combine">
  123. <title>Alle Schritte in einer Seite kombinieren</title>
  124. <para>
  125. Das folgende Beispiel kombiniert alle drei Schritte in einem Skript. Es bietet keine
  126. neuen Funktionalitäten. Der Vorteil der Verwendung eines einzelnen Skripts ist, das
  127. Entwickler keine URLs für das Skript definieren muss, das den nächsten Schritt
  128. durchführt. Standardmäßig verwenden alle Schritte die gleiche URL. Trotzdem enthält das
  129. Skript nun etwas Dispatchcode um den korrekten Code für jeden Schritt der
  130. Authentifikation aufzurufen.
  131. </para>
  132. <example id="zend.openid.consumer.example-2">
  133. <title>Das komplette Skript für ein OpenID Login</title>
  134. <programlisting language="php"><![CDATA[
  135. $status = "";
  136. if (isset($_POST['openid_action']) &&
  137. $_POST['openid_action'] == "login" &&
  138. !empty($_POST['openid_identifier'])) {
  139. $consumer = new Zend_OpenId_Consumer();
  140. if (!$consumer->login($_POST['openid_identifier'])) {
  141. $status = "OpenID Login fehlgeschlagen.";
  142. }
  143. } else if (isset($_GET['openid_mode'])) {
  144. if ($_GET['openid_mode'] == "id_res") {
  145. $consumer = new Zend_OpenId_Consumer();
  146. if ($consumer->verify($_GET, $id)) {
  147. $status = "GÜLTIG " . htmlspecialchars($id);
  148. } else {
  149. $status = "UNGÜLTIG " . htmlspecialchars($id);
  150. }
  151. } else if ($_GET['openid_mode'] == "cancel") {
  152. $status = "ABGEBROCHEN";
  153. }
  154. }
  155. ?>
  156. <html><body>
  157. <?php echo "$status<br>" ?>
  158. <form method="post">
  159. <fieldset>
  160. <legend>OpenID Login</legend>
  161. <input type="text" name="openid_identifier" value=""/>
  162. <input type="submit" name="openid_action" value="login"/>
  163. </fieldset>
  164. </form>
  165. </body></html>
  166. ]]></programlisting>
  167. </example>
  168. <para>
  169. Zusätzlich unterscheidet dieser Code zwischen abgebrochen und ungültigen
  170. Authentifizierungs Antworten. Der Provider gibt eine abgebrochene Antwort zurück, wenn
  171. der Identitäts Provider die gegebene Identität nicht unterstützt, der Benutzer nicht
  172. angemeldet ist, oder der Benutzer der Seite nicht vertraut. Eine ungültige Antwort zeigt
  173. an das die Antwort dem OpenId Protokoll nicht entspricht oder nicht korrekt signiert
  174. wurde.
  175. </para>
  176. </sect2>
  177. <sect2 id="zend.openid.consumer.realm">
  178. <title>Konsumenten Bereiche</title>
  179. <para>
  180. Wenn eine OpenID-aktivierte Site eine Authentifikations Anfrage an einen Provider
  181. übergibt, identifiziert diese sich selbst mit einer Bereichs URL. Diese URL kann als
  182. Root der vertrauten Site betrachtet werden. Wenn der Benutzer der Bereichs URL vertraut,
  183. dann sollte er oder Sie das auch bei der passenden und den untergeordneten URLs tun.
  184. </para>
  185. <para>
  186. Standardmäßig wird der Bereich automatisch auf die URL des Verzeichnisses gesetzt indem
  187. das Login Skript ist. Dieser Standardwert ist für die meisten, aber nicht alle, Fälle
  188. ausreichend. Manchmal sollte einer komplette Domain, und nicht einem Verzeichnis
  189. vertraut werden. Oder sogar einer Kombination von verschiedenen Servern in einer Domain.
  190. </para>
  191. <para>
  192. Um den Standardwert zu überschreiben müssen Entwickler die Bereichs URL als drittes
  193. Argument an die <classname>Zend_OpenId_Consumer::login</classname> Methode übergeben.
  194. Im folgenden Beispiel fragt eine einzelne Interaktion nach vertrauten Zugriff auf alle
  195. php.net Sites.
  196. </para>
  197. <example id="zend.openid.consumer.example-3_2">
  198. <title>Authentifizierungs Anfrage für spezielle Bereiche</title>
  199. <programlisting language="php"><![CDATA[
  200. $consumer = new Zend_OpenId_Consumer();
  201. if (!$consumer->login($_POST['openid_identifier'],
  202. 'example-3_3.php',
  203. 'http://*.php.net/')) {
  204. die("OpenID Login fehlgeschlagen.");
  205. }
  206. ]]></programlisting>
  207. </example>
  208. <para>
  209. Dieses Beispiel implementiert nur den zweiten Schritt der Authentifikation; der erste
  210. und dritte Schritt sind die identisch mit dem ersten Beispiel.
  211. </para>
  212. </sect2>
  213. <sect2 id="zend.openid.consumer.check">
  214. <title>Sofortige Prüfung</title>
  215. <para>
  216. In einigen Fällen muß eine Anwendung nur prüfen ob ein Benutzer bereits auf einem
  217. vertrauten OpenID Server eingeloggt ist ohne einer Interaktion mit dem Benutzer. Die
  218. <classname>Zend_OpenId_Consumer::check</classname> Methode führt genau das durch. Sie
  219. wird mit den gleichen Argumenten wie <classname>Zend_OpenId_Consumer::login</classname>
  220. ausgeführt, aber Sie zeigt dem Benutzer keine OpenID Serverseiten. Aus Sicht des
  221. Benutzers ist dieser Prozess transparent, und es scheint als ob er die Site nie
  222. verlässt. Der dritte Schritt ist erfolgreich wenn der Benutzer bereits angemeldet ist
  223. und der Site vertraut, andernfalls ist er erfolglos.
  224. </para>
  225. <example id="zend.openid.consumer.example-4">
  226. <title>Sofortige Prüfung ohne Interaktion</title>
  227. <programlisting language="php"><![CDATA[
  228. $consumer = new Zend_OpenId_Consumer();
  229. if (!$consumer->check($_POST['openid_identifier'], 'example-4_3.php')) {
  230. die("OpenID Login fehlgeschlaten.");
  231. }
  232. ]]></programlisting>
  233. </example>
  234. <para>
  235. Das Beispiel implementiert nur den zweiten Schritt der Authentifikation; der erste und
  236. dritte Schritt sind dem obigen Beispiel ähnlich.
  237. </para>
  238. </sect2>
  239. <sect2 id="zend.openid.consumer.storage">
  240. <title>Zend_OpenId_Consumer_Storage</title>
  241. <para>
  242. Es gibt drei Schritte beim Authentifizierungs Prozess von OpenID, und jeder wird durch
  243. eine separate HTTP Anfrage durchgeführt. Um die Informationen zwischen den Anfragen zu
  244. speichern verwendet <classname>Zend_OpenId_Consumer</classname> einen internen Speicher.
  245. </para>
  246. <para>
  247. Entwickler müssen sich nicht notwendigerweise um die Speicherung kümmern weil
  248. <classname>Zend_OpenId_Consumer</classname> standardmäßig einen dateibasierten Speicher
  249. im temporären Verzeichnis verwendet, ähnlich wie PHP Sessions. Trotzdem ist dieser
  250. Speicher nicht in allen Situationen richtig. Einige Entwickler wollen Informationen in
  251. einer Datenbank speichern, wärend andere einen üblichen Speicher für große Server-Farmen
  252. verwenden wollen. Glücklicherweise können Entwickler den Standardspeicher sehr einfach
  253. mit Ihrem eigenen tauschen. Um einen eigenen Speichermechanismus zu spezifizieren muß
  254. nur die <classname>Zend_OpenId_Consumer_Storage</classname> Klasse erweitert werden und
  255. diese Unterklasse dem <classname>Zend_OpenId_Consumer</classname> Konstruktor im ersten
  256. Argument übergeben werden.
  257. </para>
  258. <para>
  259. Das folgende Beispiel demonstriert einen einfachen Speicher Mechanismus der
  260. <classname>Zend_Db</classname> als sein Backend verwendet und drei Gruppen von
  261. Funktionen bereitstellt. Der erste Gruppe enthält Funktionen für die Arbeit mit
  262. Assoziationen, wärend die zweite Gruppe erkannte Informationen cacht, und die dritte
  263. Gruppe kann verwendet werden um zu prüfen ob die Antwort eindeutig ist. Die Klasse kann
  264. einfach mit bestehenden oder neuen Datenbanken verwendet werden; wenn die benötigten
  265. Tabellen nicht existieren, wird er Sie erstellen.
  266. </para>
  267. <example id="zend.openid.consumer.example-5">
  268. <title>Datenbank Speicher</title>
  269. <programlisting language="php"><![CDATA[
  270. class DbStorage extends Zend_OpenId_Consumer_Storage
  271. {
  272. private $_db;
  273. private $_association_table;
  274. private $_discovery_table;
  275. private $_nonce_table;
  276. // Übergib das Zend_Db_Adapter Objekt und die Namen der
  277. // benötigten Tabellen
  278. public function __construct($db,
  279. $association_table = "association",
  280. $discovery_table = "discovery",
  281. $nonce_table = "nonce")
  282. {
  283. $this->_db = $db;
  284. $this->_association_table = $association_table;
  285. $this->_discovery_table = $discovery_table;
  286. $this->_nonce_table = $nonce_table;
  287. $tables = $this->_db->listTables();
  288. // Erstelle die Assoziationstabellen wenn Sie nicht existieren
  289. if (!in_array($association_table, $tables)) {
  290. $this->_db->getConnection()->exec(
  291. "create table $association_table (" .
  292. " url varchar(256) not null primary key," .
  293. " handle varchar(256) not null," .
  294. " macFunc char(16) not null," .
  295. " secret varchar(256) not null," .
  296. " expires timestamp" .
  297. ")");
  298. }
  299. // Erstelle die Discoverytabellen wenn Sie nicht existieren
  300. if (!in_array($discovery_table, $tables)) {
  301. $this->_db->getConnection()->exec(
  302. "create table $discovery_table (" .
  303. " id varchar(256) not null primary key," .
  304. " realId varchar(256) not null," .
  305. " server varchar(256) not null," .
  306. " version float," .
  307. " expires timestamp" .
  308. ")");
  309. }
  310. // Erstelle die Nouncetabellen wenn Sie nicht existieren
  311. if (!in_array($nonce_table, $tables)) {
  312. $this->_db->getConnection()->exec(
  313. "create table $nonce_table (" .
  314. " nonce varchar(256) not null primary key," .
  315. " created timestamp default current_timestamp" .
  316. ")");
  317. }
  318. }
  319. public function addAssociation($url,
  320. $handle,
  321. $macFunc,
  322. $secret,
  323. $expires)
  324. {
  325. $table = $this->_association_table;
  326. $secret = base64_encode($secret);
  327. $this->_db
  328. ->query('insert into ' .
  329. $table . " (url, handle, macFunc, secret, expires) " .
  330. "values ('$url', '$handle', '$macFunc', " .
  331. "'$secret', $expires)");
  332. return true;
  333. }
  334. public function getAssociation($url,
  335. &$handle,
  336. &$macFunc,
  337. &$secret,
  338. &$expires)
  339. {
  340. $table = $this->_association_table;
  341. $this->_db->query("delete from $table where expires < " . time());
  342. $res = $this->_db->fetchRow('select handle, macFunc, secret, expires ' .
  343. "from $table where url = '$url'");
  344. if (is_array($res)) {
  345. $handle = $res['handle'];
  346. $macFunc = $res['macFunc'];
  347. $secret = base64_decode($res['secret']);
  348. $expires = $res['expires'];
  349. return true;
  350. }
  351. return false;
  352. }
  353. public function getAssociationByHandle($handle,
  354. &$url,
  355. &$macFunc,
  356. &$secret,
  357. &$expires)
  358. {
  359. $table = $this->_association_table;
  360. $this->_db->query("delete from $table where expires < " . time());
  361. $res = $this->_db
  362. ->fetchRow('select url, macFunc, secret, expires ' .
  363. "from $table where handle = '$handle'");
  364. if (is_array($res)) {
  365. $url = $res['url'];
  366. $macFunc = $res['macFunc'];
  367. $secret = base64_decode($res['secret']);
  368. $expires = $res['expires'];
  369. return true;
  370. }
  371. return false;
  372. }
  373. public function delAssociation($url)
  374. {
  375. $table = $this->_association_table;
  376. $this->_db->query("delete from $table where url = '$url'");
  377. return true;
  378. }
  379. public function addDiscoveryInfo($id,
  380. $realId,
  381. $server,
  382. $version,
  383. $expires)
  384. {
  385. $table = $this->_discovery_table;
  386. $this->_db
  387. ->query("insert into $table " .
  388. "(id, realId, server, version, expires) " .
  389. "values (" .
  390. "'$id', '$realId', '$server', $version, $expires)");
  391. return true;
  392. }
  393. public function getDiscoveryInfo($id,
  394. &$realId,
  395. &$server,
  396. &$version,
  397. &$expires)
  398. {
  399. $table = $this->_discovery_table;
  400. $this->_db->query("delete from $table where expires < " . time());
  401. $res = $this->_db
  402. ->fetchRow('select realId, server, version, expires ' .
  403. "from $table where id = '$id'");
  404. if (is_array($res)) {
  405. $realId = $res['realId'];
  406. $server = $res['server'];
  407. $version = $res['version'];
  408. $expires = $res['expires'];
  409. return true;
  410. }
  411. return false;
  412. }
  413. public function delDiscoveryInfo($id)
  414. {
  415. $table = $this->_discovery_table;
  416. $this->_db->query("delete from $table where id = '$id'");
  417. return true;
  418. }
  419. public function isUniqueNonce($nonce)
  420. {
  421. $table = $this->_nonce_table;
  422. try {
  423. $ret = $this->_db
  424. ->query("insert into $table (nonce) values ('$nonce')");
  425. } catch (Zend_Db_Statement_Exception $e) {
  426. return false;
  427. }
  428. return true;
  429. }
  430. public function purgeNonces($date=null)
  431. {
  432. }
  433. }
  434. $db = Zend_Db::factory('Pdo_Sqlite',
  435. array('dbname'=>'/tmp/openid_consumer.db'));
  436. $storage = new DbStorage($db);
  437. $consumer = new Zend_OpenId_Consumer($storage);
  438. ]]></programlisting>
  439. </example>
  440. <para>
  441. Dieses Beispiel zeigt keinen OpenID Authentifikations Code, aber dieser Code würde der
  442. gleiche sein wie der für die anderen Beispiel in diesem Kapitel.
  443. </para>
  444. </sect2>
  445. <sect2 id="zend.openid.consumer.sreg">
  446. <title>Einfache Registrations Erweiterung</title>
  447. <para>
  448. Zusätzlich zur Authentifikation kann OpenID Standard für einen leichtgewichtigen
  449. Profiltausch verwendet werden, um Informationen über einen Benutzer über mehrere Sites
  450. hinweg portabel zu machen. Dieses Feature wird nicht durch die OpenID Authentifikations
  451. Spezifikation abgedeckt, aber vom OpenID Einfachen Registrierungs Erweiterungs Protokoll
  452. unterstützt. Dieses Protokoll erlaubt es OpenID-aktivierten Sites nach Informationen
  453. über End-Benutzern von OpenID Providers zu fragen. Diese Informationen können folgendes
  454. beinhalten:
  455. </para>
  456. <itemizedlist>
  457. <listitem>
  458. <para>
  459. <emphasis>nickname</emphasis>
  460. - ein UTF-8 String den der End-Benutzer als Spitzname verwendet.
  461. </para>
  462. </listitem>
  463. <listitem>
  464. <para>
  465. <emphasis>email</emphasis>
  466. - die Email Adresse des Benutzers wie in Sektion 3.4.1 von RFC2822 spezifiziert.
  467. </para>
  468. </listitem>
  469. <listitem>
  470. <para>
  471. <emphasis>fullname</emphasis>
  472. - eine UTF-8 String Repräsentation des kompletten Namens des Benutzers.
  473. </para>
  474. </listitem>
  475. <listitem>
  476. <para>
  477. <emphasis>dob</emphasis>
  478. - das Geburtsdatum des Benutzers im Format 'YYYY-MM-DD'. Jeder Wert dessen
  479. Repräsentation weniger als die speifizierte Anzahl an Ziffern in diesem Format
  480. verwendet sollte mit Nullen aufgefüllt werden. In anderen Worten, die Länge
  481. dieses Wertes muß immer 10 sein. Wenn der Benutzer irgendeinen Teil dieses
  482. Wertes (z.B. Jahr, Monat oder Tag) nicht angeben will, dann muß dieser auf Null
  483. gesetzt werden. Wenn ein Benutzer zum Beispiel angeben will das sein
  484. Geburtsdatum in das Jahr 1980 fällt, aber nicht den Monat oder Tag angeben will,
  485. dann sollte der zurückgegebene Wert '1980-00-00' sein.
  486. </para>
  487. </listitem>
  488. <listitem>
  489. <para>
  490. <emphasis>gender</emphasis>
  491. - das Geschlecht des Benutzers: "M" für männlich, "F" für weiblich
  492. </para>
  493. </listitem>
  494. <listitem>
  495. <para>
  496. <emphasis>postcode</emphasis> - ein UTF-8 String der dem Postleitzahl System des
  497. Landes des End-Benutzers entspricht
  498. </para>
  499. </listitem>
  500. <listitem>
  501. <para>
  502. <emphasis>country</emphasis>
  503. - das Land des Wohnsitzes des Benutzers wie spezifiziert in ISO3166
  504. </para>
  505. </listitem>
  506. <listitem>
  507. <para>
  508. <emphasis>language</emphasis>
  509. - die bevorzugte Sprache des Benutzers wie spezifiziert in ISO639
  510. </para>
  511. </listitem>
  512. <listitem>
  513. <para>
  514. <emphasis>timezone</emphasis> - ein ASCII String von der Zeitzonen Datenbank.
  515. Zum Beispiel, "Europe/Paris" oder "America/Los_Angeles".
  516. </para>
  517. </listitem>
  518. </itemizedlist>
  519. <para>
  520. Eine OpenID-aktivierte Web-Seite kann nach jeder beliebigen Kombination dieser Felder
  521. fragen. Sie kann auch einige Informationen strikt fordern und es Benutzern erlauben
  522. zusätzliche Informationen anzubieten oder zu verstecken. Das folgende Beispiel
  523. Instanziiert die <classname>Zend_OpenId_Extension_Sreg</classname> Klasse die einen
  524. <emphasis>nickname</emphasis> (Spitzname) benötigt und optional eine
  525. <emphasis>email</emphasis> (E-Mail) und einen <emphasis>fullname</emphasis>
  526. (vollständigen Namen) benötigt.
  527. </para>
  528. <example id="zend.openid.consumer.example-6_2">
  529. <title>Anfragen mit einer einfachen Registrations Erweiterung senden</title>
  530. <programlisting language="php"><![CDATA[
  531. $sreg = new Zend_OpenId_Extension_Sreg(array(
  532. 'nickname'=>true,
  533. 'email'=>false,
  534. 'fullname'=>false), null, 1.1);
  535. $consumer = new Zend_OpenId_Consumer();
  536. if (!$consumer->login($_POST['openid_identifier'],
  537. 'example-6_3.php',
  538. null,
  539. $sreg)) {
  540. die("OpenID Login fehlgeschlagen.");
  541. }
  542. ]]></programlisting>
  543. </example>
  544. <para>
  545. Wie man sieht akzeptiert der <classname>Zend_OpenId_Extension_Sreg</classname>
  546. Konstruktor ein Array von OpenId Feldern. Das Array hat den Namen der Felder als Indezes
  547. zu einem Flag das anzeigt ob das Feld benötigt wird oder nicht.
  548. <emphasis>true</emphasis> bedeutet der Wert wird benötigt und <emphasis>false</emphasis>
  549. bedeutet das Feld ist optional. Die Methode
  550. <classname>Zend_OpenId_Consumer::login</classname> akzeptiert eine Erweiterung oder ein
  551. Array von Erweiterungen als sein viertes Argument.
  552. </para>
  553. <para>
  554. Im dritten Schritt der Authentifikation sollte das
  555. <classname>Zend_OpenId_Extension_Sreg</classname> Objekt an
  556. <classname>Zend_OpenId_Consumer::verify</classname> übergeben werden. Anschließend wird
  557. die Methode <classname>Zend_OpenId_Extension_Sreg::getProperties</classname>, bei
  558. erfolgreicher Authentifizierung, ein assoziatives Array von benötigten Feldern
  559. zurückgeben.
  560. </para>
  561. <example id="zend.openid.consumer.example-6_3">
  562. <title>Antworten mit einer einfachen Registierungs Erweiterung prüfen</title>
  563. <programlisting language="php"><![CDATA[
  564. $sreg = new Zend_OpenId_Extension_Sreg(array(
  565. 'nickname'=>true,
  566. 'email'=>false,
  567. 'fullname'=>false), null, 1.1);
  568. $consumer = new Zend_OpenId_Consumer();
  569. if ($consumer->verify($_GET, $id, $sreg)) {
  570. echo "GÜLTIG " . htmlspecialchars($id) . "<br>\n";
  571. $data = $sreg->getProperties();
  572. if (isset($data['nickname'])) {
  573. echo "Spitzname: " . htmlspecialchars($data['nickname']) . "<br>\n";
  574. }
  575. if (isset($data['email'])) {
  576. echo "Email: " . htmlspecialchars($data['email']) . "<br>\n";
  577. }
  578. if (isset($data['fullname'])) {
  579. echo "Vollständiger Name: " . htmlspecialchars($data['fullname'])
  580. . "<br>\n";
  581. }
  582. } else {
  583. echo "UNGÜLTIG " . htmlspecialchars($id);
  584. }
  585. ]]></programlisting>
  586. </example>
  587. <para>
  588. Wenn das <classname>Zend_OpenId_Extension_Sreg</classname> Objekt ohne Argumente
  589. erstellt wurde, sollte der Benutzercode selbst das Vorhandensein der benötigten Daten
  590. prüfen. Trotzdem, wenn das Objekt mit der gleichen Liste an benötigten Feldern wie im
  591. zweiten Schritt erstellt wird, wird es automatisch die Existenz der benötigten Daten
  592. prüfen. In diesem Fall wird <classname>Zend_OpenId_Consumer::verify</classname>
  593. <emphasis>false</emphasis> zurückgeben wenn irgendeines der benötigten Felder fehlt.
  594. </para>
  595. <para>
  596. <classname>Zend_OpenId_Extension_Sreg</classname> verwendet standardmäßig die Version
  597. 1.0 weil die Spezifikation der Version 1.1 noch nicht fertiggestellt wurde. Trotzdem
  598. unterstützen einige Bibliotheken die Version 1.0 nicht vollständig. Zum Beispiel
  599. benötigt www.myopenid.com einen SREG Namensraum in den Anfragen der nur in 1.1 vorhanden
  600. ist. Um mit so einem Server zu Arbeiten muß man die Version 1.1 explizit im
  601. <classname>Zend_OpenId_Extension_Sreg</classname> Konstruktor setzen.
  602. </para>
  603. <para>
  604. Das zweite Argument des <classname>Zend_OpenId_Extension_Sreg</classname> Konstruktors
  605. ist eine Policy URL, die dem Benutzer durch den Identitäts Provider zur Verfügung
  606. gestellt werden sollte.
  607. </para>
  608. </sect2>
  609. <sect2 id="zend.openid.consumer.auth">
  610. <title>Integration mit Zend_Auth</title>
  611. <para>
  612. Zend Framework bietet eine spezielle Klasse für die Unterstützung von Benutzer
  613. Authentifikation: <classname>Zend_Auth</classname>. Diese Klasse kann zusammen mit
  614. <classname>Zend_OpenId_Consumer</classname> verwendet werden. Das folgende Beispiel
  615. zeigt wie <code>OpenIdAdapter</code> das
  616. <classname>Zend_Auth_Adapter_Interface</classname> mit der <code>authenticate</code>
  617. Methode implementiert. Diese führt eine Authentifikations Anfrage und Verifikation
  618. durch.
  619. </para>
  620. <para>
  621. Der große Unterschied zwischen diesem Adapter und dem bestehenden ist, das er mit zwei
  622. HTTP Anfragen arbeitet und einen Dispatch code enthält um den zweiten oder dritten
  623. Schritt der OpenID Authentifikation durchzuführen.
  624. </para>
  625. <example id="zend.openid.consumer.example-7">
  626. <title>Zend_Auth Adapter für OpenID</title>
  627. <programlisting language="php"><![CDATA[
  628. class OpenIdAdapter implements Zend_Auth_Adapter_Interface {
  629. private $_id = null;
  630. public function __construct($id = null) {
  631. $this->_id = $id;
  632. }
  633. public function authenticate() {
  634. $id = $this->_id;
  635. if (!empty($id)) {
  636. $consumer = new Zend_OpenId_Consumer();
  637. if (!$consumer->login($id)) {
  638. $ret = false;
  639. $msg = "Authentifizierung fehlgeschlagen.";
  640. }
  641. } else {
  642. $consumer = new Zend_OpenId_Consumer();
  643. if ($consumer->verify($_GET, $id)) {
  644. $ret = true;
  645. $msg = "Authentifizierung erfolgreich";
  646. } else {
  647. $ret = false;
  648. $msg = "Authentifizierung fehlgeschlagen";
  649. }
  650. }
  651. return new Zend_Auth_Result($ret, $id, array($msg));
  652. }
  653. }
  654. $status = "";
  655. $auth = Zend_Auth::getInstance();
  656. if ((isset($_POST['openid_action']) &&
  657. $_POST['openid_action'] == "login" &&
  658. !empty($_POST['openid_identifier'])) ||
  659. isset($_GET['openid_mode'])) {
  660. $adapter = new OpenIdAdapter(@$_POST['openid_identifier']);
  661. $result = $auth->authenticate($adapter);
  662. if ($result->isValid()) {
  663. Zend_OpenId::redirect(Zend_OpenId::selfURL());
  664. } else {
  665. $auth->clearIdentity();
  666. foreach ($result->getMessages() as $message) {
  667. $status .= "$message<br>\n";
  668. }
  669. }
  670. } else if ($auth->hasIdentity()) {
  671. if (isset($_POST['openid_action']) &&
  672. $_POST['openid_action'] == "logout") {
  673. $auth->clearIdentity();
  674. } else {
  675. $status = "Du bist angemeldet als " . $auth->getIdentity() . "<br>\n";
  676. }
  677. }
  678. ?>
  679. <html><body>
  680. <?php echo htmlspecialchars($status);?>
  681. <form method="post"><fieldset>
  682. <legend>OpenID Login</legend>
  683. <input type="text" name="openid_identifier" value="">
  684. <input type="submit" name="openid_action" value="login">
  685. <input type="submit" name="openid_action" value="logout">
  686. </fieldset></form></body></html>
  687. ]]></programlisting>
  688. </example>
  689. <para>
  690. Mit <classname>Zend_Auth</classname> wird die Identität des End-Benutzes in den Session
  691. Daten gespeichert. Sie kann mit <classname>Zend_Auth::hasIdentity</classname> und
  692. <classname>Zend_Auth::getIdentity</classname> geprüft werden.
  693. </para>
  694. </sect2>
  695. <sect2 id="zend.openid.consumer.mvc">
  696. <title>Integration mit Zend_Controller</title>
  697. <para>
  698. Zuletzt ein paar Worte über die Integration in Model-View-Controller Anwendungen: Solche
  699. Zend Framework Anwendungen werden implementiert durch Verwenden der
  700. <classname>Zend_Controller</classname> Klasse und Sie verwenden die
  701. <classname>Zend_Controller_Response_Http</classname> Klasse um HTTP Antworten
  702. vorzubereiten und an den Web Browser des Benutzers zurückzusenden.
  703. </para>
  704. <para>
  705. <classname>Zend_OpenId_Consumer</classname> bietet keine GUI Möglichkeiten aber es führt
  706. HTTP Umleitungen bei erflgreichen <classname>Zend_OpenId_Consumer::login</classname> und
  707. <classname>Zend_OpenId_Consumer::check</classname> durch. Diese Umleitungen könnten
  708. nicht richtig funktionieren, oder sogar überhaupt nicht, wenn einige Daten bereits an
  709. den Web Browser gesendet wurden. Um HTTP Umleitungen im MVC Code richtig durchzuführen
  710. sollte die echte <classname>Zend_Controller_Response_Http</classname> als letztes
  711. Argument an <classname>Zend_OpenId_Consumer::login</classname> oder
  712. <classname>Zend_OpenId_Consumer::check</classname> gesendet werden.
  713. </para>
  714. </sect2>
  715. </sect1>
  716. <!--
  717. vim:se ts=4 sw=4 et:
  718. -->