[Java] LDAP Anbindung für Login & Berechtigungen – Tutorial

In letzter Zeit musste ich mich öfters mit LDAP Anbindungen rumschlagen, weshalb ich euch heute ein kleines Tutorial geben möchte, wie man eine Java Applikation an einen LDAP Server auf einfache Art & Weise anbindet. Da es hierzu weder wirklich gute englische, noch deutsche Tutorials gibt (bzw. mir damals keins so richtig weitergeholfen hatte ohne einen ganzen Roman zu lesen), habe ich selbst eins geschrieben.

"<yoastmark

 

Was ist LDAP überhaupt? Wieso sollte ich das nutzen?

Wikipedia sagt dazu folgendes:

“Das Lightweight Directory Access Protocol (LDAP), deutsch etwa Leichtgewichtiges Verzeichniszugriffsprotokoll, ist ein Netzwerkprotokoll zur Abfrage und Änderung von Informationen verteilter Verzeichnisdienste. ”

Quelle: https://de.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol

 

Das sagt einem natürlich erstmal nur relativ wenig, außer dass dieses Protokoll mit Verteichnissen arbeitet. LDAP ist so gesehen also auch eine Art Verzeichnisprotokoll für bestimmte Datenbanken, welches auf spezielle Bedürfnisse zugeschnitten ist. Um das ganze etwas abzukürzen:

LDAP kommt vorallem als Adressbuch-Verwaltungen, Benutzerverwaltung (z.B. authentifizieren Windows Rechner im Unternehmen normalerweise gegen einen LDAP Server im Active Directory), sowie für Benutzerdaten für Mailserver (SMTP, POP und IMAP). Wenn man also in einem Unternehmen 3000 Rechner hat und der Nutzer sich überall einloggen können soll, müssen die Logindaten natürlich irgendwo gespeichert werden. Man könnte natürlich den User auf allen Rechnern (die er benutzt) einzeln anlegen, aber das wäre ziemlich umständlich. Stattdessen legt man den Nutzer im Active Directory an und Windows authentifiziert dann einfach gegen das LDAP. Dadurch muss man den User lediglich einmal anlegen und er kann sich an jedem Rechner anmelden (dies kann man natürlich auch auf Rechner-Gruppen beschränken – dank der Verzeichnisse). Aber auch bei Linux kommt LDAP (und Radius) immer öfter zum Einsatz. So gilt für Linux Server natürlich das selbe wie für Windows Rechner, die Administratoren sollen sich überall einloggen können, man kann nicht bei jedem Server den User einzeln anlegen. Und Dienste können LDAP ebenfalls nutzen, z.B. Foren, Wikis, eigene Systeme usw.

Da LDAP an sich genau genommen keine Datenbank, sondern lediglich das Protokoll ist, gibt es diverse Implementierungen, z.B. Windows Active Directory, OpenLDAP und Apache DS. LDAP hilft einem also irgendwo bei der Organisation der Benutzerdaten und um diese Daten zu “zentrialisieren”. Ihr solltet LDAP also immer dann nutzen, wenn sich ein Nutzer mit den selben Logindaten an mehreren Systemen anmelden können soll oder ihr aber die Logindaten aus Sicherheitsgründen nicht in eurer eigenen Datenbank speichern wollt.

 

LDAP in Java

Nachdem wir eine kleine Vorahnung haben, was LDAP eig. ist, wollen wir uns einmal daran versuchen, eine Anbindung für einen Login zu schreiben. Dies ist übrigens mit fast jeder Programmiersprache möglich, so habe ich in meinem PHP CMS System “RocketCMS” (ehemals “JuKuCMS“) ebenfalls einen LDAP Login als Plugin implementiert, damit auch Enterprise Kunden (Unternehmen) dieses CMS später vllt. einmal einsetzen könnten.

Leider ist LDAP gerade für Neulinge meist gar nicht so einfach zu verstehen und ich selbst bin auch kein LDAP Experte. Und es gibt zahlreiche Java LDAP Client Libraries, aber eig. hat Java alles nötige bereits mit an Bord (im Package “javax.naming“). Glücklicherweise gibt es auch öffentliche LDAP Server, sodass wir erstmal gar keinen eigenen installieren müssen. Wir erstellen also einfach eine Klasse LDAPLogin und fangen einfach mal an.

 

Die Methode login() soll prüfen, ob die Zugangsdaten stimmen und ob der Nutzer das Recht besitzt, diesen Service (die entsprechende Anwendung) überhaupt nutzen zu dürfen.

Achtung! Im öffentlichen LDAP Server haben die Nutzer keine Gruppen (Berechtigungen), dort könnt ihr also nur den Login selbst testen und müsst den Permission Check ausklammern.

 

Hier bereiten wir die Verbindung vor, setzen die IP, den Port, den User und das Passwort, sowie sonstige Properties. Es gibt 2 Möglichkeiten die Logindaten zu prüfen:

1. Man loggt sich mit einem Administrator User in den LDAP Server ein, sucht den Nutzer und prüft das Passwort oder 2. man loggt sich mit dem User selbst in den LDAP Server ein und kann dann standardmäßig seine eigenen Eigenschaften sehen. Die Prüfung des Passworts übernimmt der Server dann selbst für uns. Im Tutorial werden wir die 2. Variante der Einfachhheit halber verwenden.

Desweiteren generieren wir den UserDN: String userDn = this.userPrefix + username.replace(“,”, “”) + this.userSuffix; aus einem ganz einfachen Grund:
Man kann sich nicht einfach mit seinem Username & Passwort am LDAP Server anmelden, sondern da LDAP auf Verzeichnisse ausgelegt ist, müssen wir das Verzeichnis auch noch mit angeben. Der fertige userDN sieht dann z.B. so aus: “uid=<Username>,dc=example,dc=com“.

D.h. genauer gesagt suchen wir nach einem “Objekt” mit der Eigenschaft “uid” (User ID) mit dem Username als Wert im Ordner “com/example” (“dc=example,dc=com”, man spricht hier auch von der Domäne).

Für jeden Unterordner wird ein neues “dc=<Ordner>” spezifiziert, d.h. nicht “dc=com/example”, sondern eben “dc=example,dc=com”, wobei der Root ganz hinten ist.

Mehr zum Thema DC, OU und CN gibts hier.

 

Verbinden wir also zum Server und prüfen, ob das Passwort stimmt:

Diesen Code können wir jetzt bereits gegen den Public LDAP Server mit dem Username “riemann” und dem Passwort “password” testen, wenn wir in der Klasse die Variable wie folgt abändern:

“ou” steht hier für die Gruppe. D.h. wir suchen nach dem Nutzer “riemann” in der Gruppe “mathematicians” im Verzeichnis “com/example” und prüfen das Passwort.

Wenn hier nicht true zurückgegeben wird, hast du irgendwas falsch gemacht. Es sollte funktionieren.

 

Als letztes wollen wir noch die Gruppen des Nutzers auslesen um die Berechtigung zu prüfen. In unserem Fall steht jede Gruppe / Rolle für eine Berechtigung:

Da dieser Code doch etwas komplizierter ist und es den Rahmen dieses Tutorials sprengen würde, wenn ich ganz genau darauf eingehen würde, hier nur ein paar Anmerkungen:

  1. Die meisten Linux LDAP Server implementieren das Attribut “memberof” oder “member” nicht beim User standardmäßig. D.h. viele Lösungen auf Stackoverflow werden bei euch nicht funktionieren. Statt also den User zu suchen und die Gruppen-Attribute einfach auszulesen, müssen wir die Gruppen suchen, in denen der User Mitglied ist.
  2. Mit ctx.search() starten wir die Suche und erhalten eine Enumeration der Ergebnisse zurück. Da wir nur den Gruppennamen selbst wissen wollen und nicht das Verzeichnis, haben wir nur das Attribute “cn” ausgewählt und dieses mit

    als einzigen Rückgabewert gesetzt.
  3. Mit

    geben wir an, dass er nicht nur nach Gruppen im Verzeichnis (Domäne) “com/example” suchen soll, sondern auch in deren Unterverzeichnissen, z.B. “com/example/my-subdirectory”.
  4. Die Variable “usersContainer” enthält den Verzeichnisnamen, in dem er suchen soll. Konkret ist das (meist) der selbe wie userSuffix, nur ohne Komma vorne dran. Also z.B. “dc=example,dc=com“.
  5. Das 2. Argument von ctx.search() ist der Filter. Der Filter ist meines Erachtens nach eines der komplexesten Themen von LDAP. In unserem Fall sucht der Filter

    nach Objekten mit der Eigenschaften “memberUid=<Username>” (also Gruppen in denen der Nutzer Mitglied ist) oder – das ist eben von der LDAP Implementierung abhängig – nach dem Nutzer selbst und dessen Gruppen. Für unser Beispiel könnten wir den Filter auch abkürzen:

    Wichtig! Wie ich selbst feststellen musste, wird der Username in den Gruppen klein geschrieben.
  6. In der while-Schleife gehen wir alle Ergebnisse durch, extrahieren das Attribut “cn” (Gruppenname in diesem Fall) und fügen dies der Liste hinzu.
  7. Am Ende geben wir die Liste mit den Gruppennamen zurück.

 

Jetzt ergänzen wir in der Methode login() nur noch die folgenden Zeilen, um zu prüfen, ob der User Mitglied in der Berechtigungsgruppe ist:

 

Und schon haben wir auch noch einen Permissioncheck und sind fertig. 🙂
Fragen, Kritik & Kommentare könnt ihr wie immer unten als Kommentar hinterlassen. Vielen Dank fürs Lesen!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.