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