Zend_OpenId-Consumer.xml 33 KB

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