2008-12-23 5 views
63

Ich habe eine einfache Aufgabe der Authentifizierung gegen Active Directory mit Java. Nur Anmeldeinformationen überprüfen und sonst nichts. Nehmen wir an, meine Domain ist "fun.xyz.tld", der OU-Pfad ist unbekannt und der Benutzername/das Passwort ist testu/testp.Authentifizierung gegen Active Directory mit Java unter Linux

Ich weiß, es gibt ein paar Java-Bibliotheken, die diese Aufgabe vereinfachen, aber ich war nicht erfolgreich bei der Implementierung von ihnen. Die meisten Beispiele, die ich gefunden habe, adressieren LDAP im Allgemeinen, nicht speziell Active Directory. Das Ausstellen einer LDAP-Anfrage bedeutet, dass darin ein OU-Pfad gesendet wird, den ich nicht habe. Außerdem sollte die Anwendung, die die LDAP-Anfrage absetzt, bereits an Active Directory gebunden sein, um darauf zugreifen zu können ... Unsicher, da die Zugangsdaten irgendwo auffindbar gespeichert werden müssten. Ich würde gerne eine Testbindung mit Test-Credentials, wenn möglich - das würde bedeuten, dass das Konto gültig ist.

Zuletzt, wenn möglich, gibt es eine Möglichkeit, einen solchen Authentifizierungsmechanismus verschlüsselt zu machen? Ich weiß, dass AD Kerberos verwendet, aber nicht sicher, ob Javas LDAP-Methoden dies tun.

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

Antwort

46

installieren Hier ist der Code, den ich basierend auf Beispiel aus diesem Blog zusammengestellt: LINK und diese 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(); 
    } 

} 
+3

'import com.sun.jndi.ldap.LdapCtxFactory;' - Dies wird höchstwahrscheinlich nur mit einer Sun JVM funktionieren. –

3

Überprüfen Sie nur Anmeldeinformationen? In diesem Fall könnten Sie einfach kerberos tun und sich nicht mit LDAP befassen.

+0

Ja, nur Anmeldeinformationen überprüfen. Ich habe die Frage mit Klarstellung bearbeitet. Unterscheidet sich der Code von der LDAP-Authentifizierung? –

6

Ich habe gerade ein Projekt abgeschlossen, das AD und Java verwendet. Wir verwendeten Spring ldapTemplate.

AD ist LDAP-kompatibel (fast), 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, egal, ob Sie nur eine Verbindung herstellen möchten.

Ich würde einen Blick auf: Spring LDAP

Sie Beispiele zu haben.

Wie für die Verschlüsselung verwendeten wir SSL-Verbindung (so war es LDAPS). AD musste auf einem SSL-Port/Protokoll konfiguriert werden.

Stellen Sie zunächst sicher, dass Sie über eine LDAP-IDE eine Verbindung zu Ihrem AD herstellen können. Ich benutze , es ist wirklich cool, und es ist in Java geschrieben. Das ist alles was ich brauchte. Zu Testzwecken könnten Sie auch Apache Directory Server

+0

Luchiani, ich bin zur Zeit entwickeln Web-Anwendung zur Integration von Java-Frühling zu teilen (Windows), meins, während ich nicht in der Lage, Benutzer im Active Directory mit Java-Code erstellen, können Sie Ihren Code für Benutzer im Active Directory mit Kommentar freigeben damit ich pünktlich weiterarbeiten kann. –

90

Es gibt 3 Authentifizierungsprotokolle, die verwendet werden können Authentifizierung zwischen Java und Active Directory auf Linux oder einer anderen Plattform (und diese sind nicht auf HTTP-Dienste nur spezifisch) auszuführen:

  1. Kerberos - Kerberos bietet Single Sign-On (SSO) und Delegation, 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 - Mit einer LDAP-Bindung können Sie einen Kontonamen und ein Kennwort einfach validieren.

Es auch als etwas, das „ADFS“, die SSO für Websites bietet SAML verwenden, die in der Windows-SSP ruft so in der Praxis ist es im Grunde eine Umwegen eine der anderen oben genannten Protokolle zu verwenden.

Jedes Protokoll hat seine Vorteile, aber als Faustregel sollte man im Allgemeinen versuchen, "so zu machen, wie Windows es tut". Was macht Windows?

Zunächst bevorzugt die Authentifizierung zwischen zwei Windows-Maschinen Kerberos, da Server nicht mit dem Domänencontroller kommunizieren müssen und Clients Kerberos-Tickets zwischenspeichern können, was die Belastung der Domänencontroller verringert (und weil Kerberos die Delegierung unterstützt).

Wenn jedoch die authentifizierenden Parteien nicht über Domänenkonten verfügen oder wenn der Client nicht mit dem Domänencontroller kommunizieren kann, ist NTLM erforderlich. Daher schließen sich Kerberos und NTLM nicht gegenseitig aus und NTLM wird nicht von Kerberos veraltet. Tatsächlich ist NTLM in mancher Hinsicht besser als Kerberos. Beachten Sie, dass wenn ich Kerberos und NTLM im selben Atemzug erwähne, auch SPENGO und Integrated Windows Authentication (IWA) erwähnen muss. IWA ist ein einfacher Begriff, der im Wesentlichen Kerberos oder NTLM oder SPNEGO zum Aushandeln von Kerberos oder NTLM bedeutet.

Die Verwendung einer LDAP-Bindung als Möglichkeit zur Validierung von Anmeldeinformationen ist nicht effizient und erfordert SSL. Aber bis vor kurzem die Implementierung von Kerberos und NTLM war schwierig, so dass die Verwendung von LDAP als Make-Shift-Authentifizierungsdienst beibehalten hat. Aber an dieser Stelle sollte es im Allgemeinen vermieden werden. LDAP ist ein Verzeichnis von Informationen und kein Authentifizierungsdienst. Verwenden Sie es für den beabsichtigten Zweck.

Wie also implementieren Sie Kerberos oder NTLM in Java und insbesondere im Kontext von Webanwendungen?

Es gibt eine Reihe großer Unternehmen wie Quest Software und Centrify, die Lösungen speziell für Java anbieten. Ich kann diese nicht wirklich kommentieren, da es sich um unternehmensweite "Identitätsmanagement-Lösungen" handelt. Aus dem Blick auf den Marketing-Spin auf ihrer Website ist es schwer zu sagen, welche Protokolle genau verwendet werden und wie. Sie müssen sie für die Details kontaktieren.

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

Für NTLM gibt es ein freies OSS-Projekt namens JCIFS mit einem HTTP-Authentifizierungs-Servletfilter für NTLM. Es verwendet jedoch eine Man-in-the-Middle-Methode, um die Anmeldeinformationen mit einem SMB-Server zu überprüfen, der nicht mit NTLMv2 funktioniert (was langsam zu einer erforderlichen Domänensicherheitsrichtlinie wird). Aus diesem Grund und anderen wird der HTTP-Filter-Teil von JCIFS entfernt. Beachten Sie, dass es mehrere 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 einzige korrekte Möglichkeit, NTLM-Anmeldeinformationen mit Active Directory zu validieren, besteht darin, den NetrLogonSamLogon DCERPC-Aufruf über NETLOGON mit Secure Channel zu verwenden. Gibt es so etwas in Java? Ja.Hier ist sie:

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

Jespa ist eine 100% Java-Implementierung, die NTLM unterstützt NTLMv2, NTLMv1, volle Integrität und Vertraulichkeit Optionen und die zuvor erwähnte NETLOGON Credential-Validierung. 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 mehr.

Mike

+3

Update: HttpClient unterstützt jetzt Basic-, Digest-, NTLMv1-, NTLMv2-, NTLM2-Sitzung-, SNPNEGO- und Kerberos-Authentifizierungsschemas. –

2

Wenn alles, was Sie tun möchten, ist gegen AD zu authentifizieren Kerberos verwenden, dann ein einfaches http://spnego.sourceforge.net/HelloKDC.java Programm sollte es tun.

Werfen Sie einen Blick auf die "Pre-Flight" -Dokumentation des Projekts, die über das Programm HelloKDC.java spricht.

4

Wie ioplex und andere gesagt haben, gibt es viele Möglichkeiten. Verwendung von LDAP (und das Novell LDAP API) zu authentifizieren, habe ich verwendet, so etwas wie:


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

Als „Besonderheit“, ermöglicht Active Directory bindet LDAP gegen „user @ domain“ ohne den eindeutigen Namen der Verwendung Konto. Dieser Code verwendet StartTLS, um die TLS-Verschlüsselung für die Verbindung zu aktivieren. Die andere Alternative ist LDAP über SSL, die von meine AD-Server nicht unterstützt wird.

Der wahre Trick besteht darin, den Server und den Host zu finden; Der offizielle Weg besteht darin, einen DNS SRV (Service) Record Lookup zu verwenden, um ein Bündel von möglichen Hosts zu finden, und dann einen UDP-basierten LDAP "Ping" (in einem bestimmten Microsoft Format) auszuführen, um den richtigen Server zu finden. Wenn Sie interessiert sind, habe ich einige blog articles über meine Reise von Abenteuer und Entdeckung in diesem Bereich gepostet.

Wenn Sie Kerberos-basierte Benutzername/Passwort-Authentifizierung durchführen möchten, betrachten Sie einen anderen Kessel Fisch; Es ist mit dem Java-GSS-API-Code machbar, obwohl ich nicht sicher bin, ob es den letzten Schritt zur Validierung der Authentifizierung durchführt. (Der Code, der die Validierung durchführt, kann den AD-Server kontaktieren, um den Benutzernamen und das Kennwort zu überprüfen. Dies führt zu einem Ticket-Erteilungsticket für den Benutzer. Um sicherzustellen, dass der AD-Server nicht imitiert wird, muss er auch versuchen, ein Ticket zu erhalten der Benutzer zu sich selbst, der etwas komplizierter ist.)

Wenn Sie Kerberos-basierte Single Sign-On durchführen möchten, vorausgesetzt, dass Ihre Benutzer für die Domäne authentifiziert sind, können Sie dies auch mit der Java GSS-API tun Code. Ich würde ein Codebeispiel posten, aber ich muss meinen abscheulichen Prototyp noch in etwas verwandeln, das für menschliche Augen geeignet ist. Check out some code from SpringSource für etwas Inspiration.

Wenn Sie nach NTLM (die ich gegeben wurde zu verstehen, ist weniger sicher) oder etwas anderes, nun, viel Glück.

+0

Sehr nützliche Blogeinträge. Vielen Dank! – Aerse

+0

"Benutzer @ Domäne" funktioniert gut, thx. – chris

0

empfehle ich Ihnen am AdBroker Paket des oVirt Projekt zu suchen. 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 nach dem Code unter Engine \ backend \ manager \ module \ bll \ src \ main \ java \ org \ ouvirt \ engine \ core \ bll \ adbroker

Sie können Git verwenden, um das Repository zu klonen oder durchsuchen Sie die gerrit Verknüpfung

Verwandte Themen