it-swarm.com.de

Authentifizierung gegen Active Directory mit Java unter Linux

Ich habe eine einfache Aufgabe, mich mit Java gegen Active Directory zu authentifizieren. Nur die Anmeldeinformationen überprüfen und sonst nichts. Angenommen, meine Domäne lautet "fun.xyz.tld", der OU-Pfad ist unbekannt und der Benutzername/das Kennwort lautet testu/testp. 

Ich weiß, dass es einige Java-Bibliotheken gibt, die diese Aufgabe vereinfachen, aber es gelang mir nicht, sie zu implementieren. Die meisten Beispiele, die ich gefunden habe, befassten sich mit LDAP im Allgemeinen, nicht speziell mit Active Directory. Bei der Ausgabe einer LDAP-Anforderung wird ein OU-Pfad gesendet, den ich nicht habe. Außerdem sollte die Anwendung, die eine LDAP-Anforderung ausgibt, bereits an Active Directory gebunden sein, um darauf zugreifen zu können ... Unsicher, da die Anmeldeinformationen an einem erkennbaren Ort gespeichert werden müssten. Ich möchte, wenn möglich, eine Testbindung mit Testanmeldeinformationen - dies würde bedeuten, dass das Konto gültig ist.

Gibt es schließlich eine Möglichkeit, einen solchen Authentifizierungsmechanismus verschlüsselt zu machen? Ich weiß, dass AD Kerberos verwendet, aber ich bin mir nicht sicher, ob die LDAP-Methoden von Java dies tun.

Hat jemand ein Beispiel für Funktionscode? Vielen Dank.

71
DV.

Hier ist der Code, den ich anhand eines Beispiels aus diesem Blog zusammengestellt habe: LINK und dieser Quelle: LINK .

import com.Sun.jndi.ldap.LdapCtxFactory;
import Java.util.ArrayList;
import Java.util.Hashtable;
import Java.util.List;
import Java.util.Iterator;
import javax.naming.Context;
import javax.naming.AuthenticationException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import static javax.naming.directory.SearchControls.SUBTREE_SCOPE;

class App2 {

    public static void main(String[] args) {

        if (args.length != 4 && args.length != 2) {
            System.out.println("Purpose: authenticate user against Active Directory and list group membership.");
            System.out.println("Usage: App2 <username> <password> <domain> <server>");
            System.out.println("Short usage: App2 <username> <password>");
            System.out.println("(short usage assumes 'xyz.tld' as domain and 'abc' as server)");
            System.exit(1);
        }

        String domainName;
        String serverName;

        if (args.length == 4) {
            domainName = args[2];
            serverName = args[3];
        } else {
            domainName = "xyz.tld";
            serverName = "abc";
        }

        String username = args[0];
        String password = args[1];

        System.out
                .println("Authenticating " + username + "@" + domainName + " through " + serverName + "." + domainName);

        // bind by using the specified username/password
        Hashtable props = new Hashtable();
        String principalName = username + "@" + domainName;
        props.put(Context.SECURITY_PRINCIPAL, principalName);
        props.put(Context.SECURITY_CREDENTIALS, password);
        DirContext context;

        try {
            context = LdapCtxFactory.getLdapCtxInstance("ldap://" + serverName + "." + domainName + '/', props);
            System.out.println("Authentication succeeded!");

            // locate this user's record
            SearchControls controls = new SearchControls();
            controls.setSearchScope(SUBTREE_SCOPE);
            NamingEnumeration<SearchResult> renum = context.search(toDC(domainName),
                    "(& (userPrincipalName=" + principalName + ")(objectClass=user))", controls);
            if (!renum.hasMore()) {
                System.out.println("Cannot locate user information for " + username);
                System.exit(1);
            }
            SearchResult result = renum.next();

            List<String> groups = new ArrayList<String>();
            Attribute memberOf = result.getAttributes().get("memberOf");
            if (memberOf != null) {// null if this user belongs to no group at all
                for (int i = 0; i < memberOf.size(); i++) {
                    Attributes atts = context.getAttributes(memberOf.get(i).toString(), new String[] { "CN" });
                    Attribute att = atts.get("CN");
                    groups.add(att.get().toString());
                }
            }

            context.close();

            System.out.println();
            System.out.println("User belongs to: ");
            Iterator ig = groups.iterator();
            while (ig.hasNext()) {
                System.out.println("   " + ig.next());
            }

        } catch (AuthenticationException a) {
            System.out.println("Authentication failed: " + a);
            System.exit(1);
        } catch (NamingException e) {
            System.out.println("Failed to bind to LDAP / get account information: " + e);
            System.exit(1);
        }
    }

    private static String toDC(String domainName) {
        StringBuilder buf = new StringBuilder();
        for (String token : domainName.split("\\.")) {
            if (token.length() == 0)
                continue; // defensive check
            if (buf.length() > 0)
                buf.append(",");
            buf.append("DC=").append(token);
        }
        return buf.toString();
    }

}
47
DV.

Es gibt drei Authentifizierungsprotokolle, die zur Authentifizierung zwischen Java und Active Directory unter Linux oder einer anderen Plattform verwendet werden können (und diese sind nicht nur spezifisch für HTTP-Dienste):

  1. Kerberos - Kerberos bietet Single Sign-On (SSO) und Delegierung, aber Webserver benötigen auch SPNEGO-Unterstützung, um SSO über IE zu akzeptieren.

  2. NTLM - NTLM unterstützt SSO über IE (und andere Browser, wenn sie ordnungsgemäß konfiguriert sind).

  3. LDAP - Eine LDAP-Bindung kann verwendet werden, um einfach einen Kontonamen und ein Kennwort zu überprüfen.

Es gibt auch etwas namens "ADFS", das SSO für Websites bietet, die SAML verwenden, um Windows SSP aufzurufen. In der Praxis handelt es sich also im Grunde um eine Umgehungsmöglichkeit, eines der oben genannten Protokolle zu verwenden.

Jedes Protokoll hat seine Vorteile, aber als Faustregel gilt: Um maximale Kompatibilität zu gewährleisten, sollten Sie im Allgemeinen "so wie Windows" vorgehen. Was macht Windows also?

Erstens bevorzugt die Authentifizierung zwischen zwei Windows-Computern Kerberos, da Server nicht mit dem DC kommunizieren müssen und Clients Kerberos-Tickets zwischenspeichern können, was die Belastung der DCs verringert (und weil Kerberos die Delegierung unterstützt).

Wenn die authentifizierenden Parteien jedoch nicht über Domänenkonten verfügen oder der Client nicht mit dem Domänencontroller kommunizieren kann, ist NTLM erforderlich. Kerberos und NTLM schließen sich also nicht gegenseitig aus und NTLM wird von Kerberos nicht veraltet. In gewisser Hinsicht ist NTLM besser als Kerberos. Wenn Sie Kerberos und NTLM in einem Atemzug erwähnen, müssen Sie auch SPENGO und die integrierte Windows-Authentifizierung (IWA) erwähnen. IWA ist ein einfacher Begriff, der im Wesentlichen Kerberos oder NTLM oder SPNEGO für das Aushandeln von Kerberos oder NTLM bedeutet.

Die Verwendung einer LDAP-Bindung als Möglichkeit zum Überprüfen von Anmeldeinformationen ist nicht effizient und erfordert SSL. Bis vor kurzem war die Implementierung von Kerberos und NTLM jedoch schwierig, sodass die Verwendung von LDAP als Authentifizierungsdienst für die Umstellung beibehalten wurde. An dieser Stelle sollte es jedoch generell vermieden werden. LDAP ist ein Informationsverzeichnis und kein Authentifizierungsdienst. Verwenden Sie es für den vorgesehenen Zweck.

Wie implementieren Sie Kerberos oder NTLM in Java und insbesondere im Zusammenhang mit Webanwendungen?

Es gibt eine Reihe großer Unternehmen wie Quest Software und Centrify, die über Lösungen verfügen, die speziell auf Java verweisen. Ich kann nicht wirklich dazu Stellung nehmen, da es sich um unternehmensweite "Identity-Management-Lösungen" handelt. Daher ist es schwierig, anhand der Marketing-Spin auf ihrer Website genau zu erkennen, welche Protokolle verwendet werden und wie. Sie müssen sie für die Details kontaktieren.

Die Implementierung von Kerberos in Java ist nicht besonders schwer, da die Standard-Java-Bibliotheken Kerberos über die Klassen org.ietf.gssapi unterstützen. Bis vor kurzem gab es jedoch eine große Hürde - IE sendet keine rohen Kerberos-Token, sondern SPNEGO-Token. Mit Java 6 wurde SPNEGO jedoch implementiert. Theoretisch sollten Sie in der Lage sein, einen GSSAPI-Code zu schreiben, der IE -Clients authentifizieren kann. Aber ich habe es nicht probiert. Die Sun-Implementierung von Kerberos war über die Jahre hinweg eine Komödie der Irrtümer. Aufgrund von Suns Erfahrung in diesem Bereich würde ich keine Versprechen hinsichtlich der SPENGO-Implementierung machen, bis Sie diesen Vogel in der Hand haben.

Für NTLM gibt es ein freies OSS-Projekt namens JCIFS, das über einen NTLM-HTTP-Authentifizierungs-Servlet-Filter verfügt. Es verwendet jedoch eine Man-in-the-Middle-Methode, um die Anmeldeinformationen mit einem SMB -Server zu überprüfen, der nicht mit NTLMv2 arbeitet (was langsam zu einer erforderlichen Domänensicherheitsrichtlinie wird). Aus diesem und anderen Gründen soll der HTTP-Filter-Teil von JCIFS entfernt werden. Beachten Sie, dass es eine Reihe von Spin-Offs gibt, die JCIFS verwenden, um dieselbe Technik zu implementieren. Wenn Sie also andere Projekte sehen, die behaupten, NTLM-SSO zu unterstützen, überprüfen Sie das Kleingedruckte.

Die einzig korrekte Methode zum Überprüfen von NTLM-Anmeldeinformationen mit Active Directory ist die Verwendung des NetrLogonSamLogon-DCERPC-Aufrufs über NETLOGON mit Secure Channel. Gibt es so etwas in Java? Ja. Hier ist es:

http://www.ioplex.com/jespa.html

Jespa ist eine 100% Java NTLM-Implementierung, die NTLMv2, NTLMv1, vollständige Integritäts- und Vertraulichkeitsoptionen und die zuvor erwähnte Überprüfung der NETLOGON-Berechtigungsnachweise unterstützt. Dazu gehören ein HTTP-SSO-Filter, ein JAAS-LoginModule, ein HTTP-Client, ein SASL-Client und -Server (mit JNDI-Bindung), ein generischer "Sicherheitsanbieter" zum Erstellen benutzerdefinierter NTLM-Dienste und vieles mehr.

Mike

94
user8134

Ich habe gerade ein Projekt fertiggestellt, das AD und Java verwendet. Wir haben Spring ldapTemplate verwendet.

AD ist (fast) LDAP-kompatibel. Ich glaube nicht, dass Sie Probleme mit Ihrer Aufgabe haben werden. Ich meine die Tatsache, dass es sich um AD oder einen anderen LDAP-Server handelt. Es spielt keine Rolle, ob Sie sich nur verbinden möchten.

Ich würde einen Blick darauf werfen: Spring LDAP

Sie haben auch Beispiele.

Bei der Verschlüsselung verwendeten wir eine SSL-Verbindung (also LDAPS). AD musste auf einem SSL-Port/Protokoll konfiguriert werden.

Stellen Sie jedoch zunächst sicher, dass Sie sich über eine LDAP-IDE ordnungsgemäß mit Ihrem AD verbinden können. Ich benutze Apache Directory Studio , es ist wirklich cool und in Java geschrieben. Das ist alles was ich brauchte. Zu Testzwecken können Sie auch Apache Directory Server installieren.

6

Wie Ioplex und andere bereits gesagt haben, gibt es viele Möglichkeiten. Um mich mit LDAP (und der Novell LDAP-API) zu authentifizieren, habe ich Folgendes verwendet:


LDAPConnection connection = new LDAPConnection( new LDAPJSSEStartTLSFactory() );
connection.connect(hostname, port);
connection.startTLS();
connection.bind(LDAPConnection.LDAP_V3, username+"@"+domain, password.getBytes());

Als "besonderes Feature" ermöglicht Active Directory LDAP-Bindungen an "Benutzer @ Domäne", ohne den definierten Namen des Kontos zu verwenden. Dieser Code verwendet StartTLS, um die TLS-Verschlüsselung für die Verbindung zu aktivieren. Die andere Alternative ist LDAP über SSL, das von my AD-Servern nicht unterstützt wird.

Der eigentliche Trick besteht darin, den Server und den Host zu lokalisieren. Die offizielle Methode ist die Verwendung einer DNS-SRV-Datensatzsuche (Service-Datensatz-Suche), um ein Bündel von Kandidatenhosts zu finden. Anschließend wird ein UDP-basierter LDAP-Ping (in einem bestimmten Microsoft-Format) durchgeführt, um den richtigen Server zu finden. Wenn Sie interessiert sind, habe ich einige Blogartikel über meine Abenteuer- und Entdeckungsreise in dieser Gegend veröffentlicht.

Wenn Sie die Kerberos-basierte Authentifizierung mit Benutzernamen und Kennwort durchführen möchten, sehen Sie sich einen anderen Wasserkessel an. Es ist mit dem Java-GSS-API-Code möglich, obwohl ich nicht sicher bin, ob der letzte Schritt zur Überprüfung der Authentifizierung ausgeführt wird. (Der Code, der die Validierung durchführt, kann sich an den AD-Server wenden, um den Benutzernamen und das Kennwort zu überprüfen. Dies führt dazu, dass ein Ticket für den Benutzer ausgestellt wird. Um sicherzustellen, dass der AD-Server nicht imitiert wird, muss er auch versuchen, ein Ticket zu erhalten der Benutzer sich selbst, was etwas komplizierter ist.)

Wenn Sie Kerberos-basiertes einmaliges Anmelden durchführen möchten, vorausgesetzt, Ihre Benutzer sind bei der Domäne authentifiziert, können Sie dies auch mit dem Java-GSS-API-Code tun. Ich würde ein Codebeispiel veröffentlichen, aber ich muss meinen abscheulichen Prototyp noch in etwas für das menschliche Auge passendes verwandeln. Schauen Sie sich etwas Code von SpringSource an, um Inspiration zu erhalten.

Wenn Sie nach NTLM suchen (was mir gegeben wurde, um es zu verstehen, ist weniger sicher) oder nach etwas anderem, na gut, viel Glück.

5
Tommy McGuire

Verifizieren Sie nur Ihre Anmeldeinformationen? In diesem Fall könnten Sie einfach nur kerberos verwenden und sich nicht mit LDAP beschäftigen.

3
Anthony

Wenn Sie sich nur mithilfe von Kerberos gegen AD authentifizieren möchten, sollten Sie dies mit einem einfachen http://spnego.sourceforge.net/HelloKDC.Java - Programm tun.

Schauen Sie sich die "Pre-Flight" -Dokumentation des Projekts an, in der über das Programm HelloKDC.Java gesprochen wird.

2
Pat Gonzalez
1
Mash See

die ldap-Authentifizierung ohne SSL ist nicht sicher und jeder kann die Anmeldeinformationen des Benutzers anzeigen, da der ldap-Client während des ldap-Bindungsvorgangs Benutzernamen und Kennwort überträgt. Verwenden Sie daher stets das ldaps-Protokoll . source: Ldap-Authentifizierung Active Directory in Java Spring Security mit Beispiel

1
Seema Kiran

Ich empfehle Ihnen, sich das Adbroker-Paket des Projekts oVirt anzusehen. Es verwendet Spring-Ldap und das Krb5 JAAS Login-Modul (mit GSSAPI), um sich mit Kerberos gegen Ldap-Server (Active Directory, IPA, RHDS, Tivoli-DS) zu authentifizieren. Suchen Sie den Code unter engine\backend\manager\modules\bll\src\main\Java\org\ovirt\engine\core\bll\adbroker

Sie können git verwenden, um das Repository zu klonen oder mithilfe des Links gerrit zu suchen 

0
Yair Zaslavsky