Integriertes Login für mehrere Anwendungen: mit APEX-Bordmitteln
von Bruno Unberath, Nürnberger Versicherungsgruppe

Wenn man die Apex-Landschaft mit der mitgelieferten komfortablen Apex-Schema-Authentifizierung betreibt, kommt aus den Reihen der Benutzer recht bald die Anforderung "eine einmalige Anmeldung sollte reichen", wenn man in den unterschiedlichen Anwendungen navigiert. Es gibt wohl diverse Ansätze, diesen Wunsch zu erfüllen - hier soll einer davon beschrieben werden.

Dabei gehen wir davon aus, dass die "Login"-Anwendung eine Art "Anwendungsverzeichnis" ist, aus welchem man die jeweils anderen Anwendungen erreicht.

Es werden die Apex-Authentifizierungs-Mittel verwendet, wobei folgendes Verhalten erwünscht ist:

  • Die Benutzer loggen sich einmalig in die "Login-Anwendung" ein. Wenn Sie hieraus die anderen Anwendungen ansteuern, sind die dort bereits angemeldet.
  • Wenn die Anwendungen direkt per URL (f?p=xxx:y) aufgerufen werden, erfolgt eine Umleitung zur Login-Anwendung.
  • Der angemeldete Benutzer soll in der APEX Variable APP_USER enthalten sein und somit an den üblichen Stellen auf allen Seiten angezeigt werden (und nicht etwa APEX_PUBLIC_USER oder ähnliches).
  • Sicherheitsaspekte dürfen nicht vernachlässigt werden - Allerdings beschränkt sich die hier vorgestellte Variante darauf, den Inhalt des Session-Cookies zu verschlüsseln. Andere Aspekte wie die Verwaltung von Session-Tokens oder die Begrenzung der Laufzeit einer Session (wie es Single Sign On-Server tun) sind hier nicht berücksichtigt.

Desweiteren konzentrieren wir uns hier auf den Login-Vorgang an sich - es ist natürlich naheliegend, die Anwendungen, für welche der Login gelten soll, in Datenbanktabellen dynamisch zu verwalten; dies soll jedoch nicht Inhalt dieses Dokumentes sein.

1. Schritt: Login-Anwendung erstellen und eigenes "Pass-Cookie" setzen

Erstellen Sie nun also die zentrale Login-Anwendung, wenn sie noch nicht existiert. In diesem Beispiel hat die Login-Anwendung die ID 104 - in der Praxis ist es allerdings sinnvoll, eine spezielle ID hierfür zu nutzen (bspw. 9999999). Achten Sie dann darauf, dass Sie im weiteren Verlauf an den Stellen, in welchen die 104 verwendet wird, Ihre eigene Anwednungs-ID nutzen. Navigieren Sie dann in der Login-Anwendung zur Seite 101 - dort sind bereits einige onSubmit-Prozesse enthalten - fügen Sie nun einen neuen hinzu. Wichtig ist, dass dieser neue Prozeß vor dem bereits vorhandenen Prozeß Login ausgeführt wird (Legen Sie die Sequence-Number entsprechend fest).

Neuer PL/SQL-Prozeß muss vor dem Login-Prozeß ausgeführt werdeb

Neuer Prozeß muss vor dem Prozeß "Login" ausgeführt werden

Nehmen Sie einen PL/SQL-Prozeß, nennen Sie ihn Pass-Cookie festlegen und hinterlegen Sie folgenden PL/SQL-Code:

declare
  p_text_raw   RAW(128);
  p_key        RAW(128) := utl_raw.cast_to_raw('Key88888');
  p_crypto_typ NUMBER   := dbms_crypto.des_cbc_pkcs5;
           
  p_encrypted_raw RAW(2048);
  p_decrypted_raw RAW(2048);
begin
  -- Benutzername und damit den Cookie-Inhalt verschlüsseln 
  p_text_raw := UTL_I18N.STRING_TO_RAW (lower(:P101_USERNAME), 'AL32UTF8');
  p_encrypted_raw := dbms_crypto.encrypt(
           src=> p_text_raw, 
           typ => p_crypto_typ, 
           key => p_key);
  -- Verschlüsseltes Cookie senden
  owa_util.mime_header('text/html', FALSE);
  owa_cookie.send(
    name=>'MY_PASS_COOKIE',
    value=>p_encrypted_raw
  );
exception 
  when others then null;
end; 

Der Schlüssel (in der Variable p_key) kann frei geändert werden; ebenso sind hier natürlich auch Varianten mit höherer Sicherheit, wie sich ändernde Schlüssel oder zusätzliche Session-Tokens, mit denen die Gültigkeit des Cookies serverseitig geprüft werden kann, denkbar. Dieses Beispiel soll jedoch eine sehr einfache Umgebung beschreiben; je mehr Sicherheits-Features man aufnehmen möchte, desto eher wird es sinnvoll ein, ein fertiges Single Sign On-Produkt einzusetzen.

Nach erfolgreichem Login kann nun auf die Hauptseite der Login-Anwendung, welche bspw. die Liste mit den Anwendungen enthält, verzweigt werden.

2. Schritt: Authentifizierungsschema für die einzelnen Anwendungen erstellen

Navigieren Sie nun zu einer Ihrer Anwendungen, welche am neuen Login-Verfahren teilnehmen sollen, dort zu den Gemeinsamen Komponenten und dann zu den Authentifizierungsschemas. Auf der Seite mit den vorhandenen Schemas klicken Sie dann auf die Schaltfläche Erstellen.

Völlig neues Authentifizierungsschema erstellen

Abbildung 1: Völlig neues Authentifizierungsschema erstellen

Wählen Sie Völlig neu als Erstellmethode aus, klicken Sie auf die Schaltfläche Weiter, geben Sie dem Authentifizierungsschema im dann folgenden Dialog einen Namen ( MY_PASSCOOKIE_LOGIN) und legen Sie ggfs. eine Beschreibung fest.

Die nun folgende Seiten-Sentry-Function ist besonders wichtig. Sie wird von Application Express vor jedem Seitenaufbau aufgerufen und hier werden wir nun prüfen, ob von der Login-Anwendung ein Cookie gesetzt wurde. Hinterlegen Sie also folgenden PL/SQL-Code:

DECLARE
  v               varchar2(255) := null;
  c               owa_cookie.cookie;
  l_current_sid   number;
  usrid           number;

  p_key           RAW(128) := utl_raw.cast_to_raw('Key88888');
  p_crypto_typ    NUMBER := dbms_crypto.des_cbc_pkcs5;
  p_encrypted_raw RAW(2048);
  p_decrypted_raw RAW(2048);
BEGIN
  -- cookie suchen
  c := owa_cookie.get('MY_PASS_COOKIE');
  p_encrypted_raw := c.vals(1);

  -- war Wert gesetzt?    
  if p_encrypted_raw = null then 
   -- kein gueltiges cookie von der Hauptanwendung 
   return false;
  else
   -- Username entschlüsseln:
   p_decrypted_raw := dbms_crypto.decrypt(
            src=> p_encrypted_raw, 
            typ => p_crypto_typ, 
            key => p_key);
   v := UTL_I18N.RAW_TO_CHAR (p_decrypted_raw, 'AL32UTF8');

   -- APEX Session starten
   l_current_sid := APEX_CUSTOM_AUTH.GET_SESSION_ID;
   if l_current_sid <= 0 then
    l_current_sid := APEX_CUSTOM_AUTH.GET_NEXT_SESSION_ID;
   end if;
   htmldb_application.g_instance := l_current_sid;
   APEX_CUSTOM_AUTH.DEFINE_USER_SESSION(
     p_user=>upper(v),
     p_session_id=>l_current_sid
   );
   return true;
  end if;
EXCEPTION 
   when others then return false;
END;

In dieser Funktion wird das von der Login-Anwendung gesetzte Cookie MY_PASS_COOKIE ausgelesen und entschlüsselt. Mit dem so ermittelten Usernamen wird dann eine APEX-Session aufgebaut.

Wie schon eingangs beschrieben, ist diese Lösung nicht zu 100% sicher. Fehlen würde noch die Verwaltung einer globalen Session-ID oder eines Session-Tokens, welche ebenfalls im Cookie gespeichert würde. Damit könnte das Cookie dann gegen die Einträge in einer Datenbanktabelle verifiziert werden. Zur Illustration der Vorgehensweise soll das hier beschriebene Vorgehen jedoch ausreichen.

Als Funktion für die Session-Verifizierung können Sie, da Sie eine Seiten-Sentry-Funktion hinterlegt haben, nichts eintragen. Navigieren Sie also weiter zum Ungültiges Session-Ziel. Wählen Sie dort URL aus und tragen Sie die Hauptseite der Login-Anwendung ein (hier: f?p=104:1).

Bei ungültiger Session zurück zur Login-Anwendung

Abbildung 2: Bei ungültiger Session zurück zur Login-Anwendung

Übernehmen Sie nun bis zu den Cookie-Attributen die Voreinstellung; klicken Sie also stets auf Weiter. Den folgenden Schritt machen Sie bitte nur in APEX-Versionen bis 3.x - ab APEX 4.0 überspringen Sie diesen Schritt.

Tragen Sie als Cookie-Name den Namen des verwendeten Cookies ein: MY_PASS_COOKIE.

Namen des verwendeten Cookies eintragen

Abbildung 3: Namen des verwendeten Cookies eintragen

Tragen Sie folgende URL als Abmelde-URL ein und bestätigen Sie alle Angaben.

wwv_flow_custom_auth_std.logout?p_this_flow=&APP_ID.&p_next_flow_page_sess=104:1

Nun ist das Authentifizierungsschema erstellt.

Authentifizierungsschema MY_PASSCOOKIE_LOGIN ist fertig

Abbildung 4: Authentifizierungsschema MY_PASSCOOKIE_LOGIN ist fertig

Allerdings ist das Authentifizierungsschema noch nicht aktiv. Mit Klick auf Aktuelles Element ändern aktivieren Sie es.

Authentifizierungsschema MY_PASSCOOKIE_LOGIN aktivieren

Abbildung 5: Authentifizierungsschema MY_PASSCOOKIE_LOGIN aktivieren

Testen Sie das Zusammenspiel nun. Wenn Sie sich an der Login-Anwendung angemeldet haben und dann bspw. per Link oder Listeneintrag zu dieser Anwendung (mit dem neuen Authentifizierungsschema MY_PASSCOOKIE_LOGIN) navigieren, sind Sie automatisch angemeldet.

Wenn Sie nun allerdings auf Logout klicken und dann sofort wieder per URL-Syntax ( f?p=xxx:yy) zur Anwendung navigieren, stellen Sie fest, dass Sie immer noch angemeldet sind. Grund dafür ist, dass das "Pass-Cookie" immer noch da ist - wie bereits erwähnt, arbeiten wir hier nicht mit einem Session-Token, welches beim Logout invalidiert und anhand dessen die Gültigkeit des Cookie geprüft werden könnte. Für dieses einfache Beispiel zerstören wir das Cookie daher beim Logout einfach.

Schritt 3: Session Cookie MY_PASS_COOKIE beim Logout zerstören

Die Anwendung benötigt hierzu eine eigene Logout-Seite; diese zerstört dann das Cookie MY_PASS_COOKIE. Fügen Sie der Anwendung, die das neue Login-Verfahren nutzt (nicht der Login-Anwendung) also eine neue Seite ( 99, 999 oder eine ähnliche Seitennummer) hinzu - der Name, die Seitennummner, das verwendete Template und andere Dinge sind dabei beliebig.

Fügen Sie dieser Seite einen onLoad-Prozeß vom Typ PL/SQL hinzu.

OnLoad-Prozeß zum Zerstören des Cookie hinzufügen

Abbildung 6: OnLoad-Prozeß zum Zerstören des Cookie hinzufügen

Nehmen Sie folgenden PL/SQL-Code:

begin
 -- cookie zerstoeren und logout
 owa_util.mime_header('text/html', FALSE);
 owa_cookie.remove(
  name => 'MY_PASS_COOKIE',
  val  => null
 );
 apex_application.g_unrecoverable_error := true;
 wwv_flow_custom_auth_std.logout(
  p_this_flow           => :APP_ID,
  p_next_flow_page_sess => '104:1'
 );
exception 
 when others then raise;
end;

In dieser Prozedur wird unser Cookie sofort zerstört und anschließend wird das Standard-Logout aufgerufen. Immer wenn man in das Standard-Logout eingreift, muss man die globale Variable g_unrecoverable_error setzen.

Da der Prozess bereits beim Laden vor Header ausgeführt wird, gelangt die Seite 99 nie zur Anzeige. Sie dient lediglich dazu, die Logout-Prozedur mit Anwender-DML "anzureichern". Diese Seite 99 müssen Sie in alle Anwendungen kopieren, die das integrierte Login verwenden, nicht aber in die Login-Anwendung.

Damit Sie nun tatsächlich beim Abmelden über diesen Prozess laufen, muss das Authentifizierungsschema MY_PASSCOOKIE_LOGIN angepasst werden. Begeben Sie sich also wieder über die Gemeinsamen Komponenten in das Schema und dort zu dem Punkt Abmelde-URL. Ersetzen Sie vorhandene Url mit:

f?p=&APP_ID.:99:&SESSION.

Von nun an verzweigen Sie zum Abmelden immer auf die Seite 99 der aktuellen Anwendung. Wenden Sie die Änderungen an und testen Sie die Anwendung. Nach einem Klick auf Logout ist der Logout auch wirklich erfolgt - wählt man die Anwendung nochmals per URL-Syntax an, so gelangt man dennoch zum zentralen Login.

Schritt 4: Rücknavigation zum Anwendungsverzeichnis zulassen

Wenn es in der Praxis erwünscht ist, dass aus den nachgelagerten Anwendungen immer wieder in das "Anwendungsverzeichnis" der Login-Anwendung zurückverzweigt werden soll (um von dort aus die Anwendung zu wechseln), so sind noch kleinere Änderungen nötig.

Mit der URL f?p=104:1:&SESSION. kommen Sie von jeder beliebigen Stelle aus zum Anwendungsverzeichnis (bspw. per Eintrag in die Navigationsliste). Allerdings verwendet die Login-Anwendung noch das standardmäßige APEX-Authentifizierungsschema. Navigieren Sie nun also in der Login-Anwendung zu den Gemeinsamen Komponenten, dort zu den Authentifizierungsschemas und dort in die Eigenschaften des Schemas Application Express (Aktuell).

Hinterlegen Sie den PL/SQL-Code, welchen Sie oben als Seiten-Sentry-Funktion eingetragen haben, auch hier. Tauschen Sie weiterhin die Abmelde-URL aus: f?p=&APP_ID:99:&SESSION. - wie oben. Alle anderen Einstellungen des Authentifizierungsschemas bleiben gleich. Natürlich müssen Sie die Seite 99 nun - analog zur Vorgehensweise in der Ziel-Anwendung - auch in der Login-Anwendung anlegen.

Zusammenfassung

Sie haben in der zentralen Login-Anwendung einen Verarbeitungsprozess erstellt, in dem Sie ein Cookie setzen. Für die nachgelagerten Anwendungen wurde ein eigenes Authentifizierungsschema erstellt; dieses Schema wird in allen "Nicht-Login-Anwendungen" als das aktuelle Schema eingestellt und lässt die Ausführung der Anwendungsseiten zu, sobald sich der User an der "Login-Anwendung" angemeldet hat. Um das Cookie zu zerstören, haben Sie in der abhängigen Anwendung eine Seite 99 erstellt, die ein "angereichertes" Logout enthält.

Um eine weitere Anwendung in dieses Login-Verfahren einzubinden, müssen Sie ...

  • Das Authentifizierungsschema kopieren - dazu können Sie die Varinate Als Kopie eines vorhandenen Authentifizierungsschemas nutzen.
  • Die Seite 99 anlegen (oder kopieren)

Zum Abschluß noch ein Tipp:

Wenn Sie das Authentifizierungsschema in andere Anwendungen als Kopie übernehmen, können Sie unter Punkt Subscription bestimmen, dass es beim Ursprungsschema abonniert ist.

Zurück zur Community-Seite

Bruno Unberath ist seit 1991 Anwendungsentwickler bei der Nürnberger Versicherungsgruppe. Nach etlichen Jahren Großrechnererfahrung, verlagerte sich sein Tätigkeitsschwerpunkt in das J2EE- und Oracle-Umfeld. Zur Zeit betreut er die Oracle-Belange in einem großen Projekt; er beschäftigt sich auch mit Datenbank-basierten Anwendungen - insbesondere APEX."