Autentificarea față de Active Directory cu Java pe Linux (Programare, Java, Autentificare, Director Activ, Ldap)

DV. a intrebat.
a intrebat.

Am o sarcină simplă de autentificare față de Active Directory folosind Java. Doar verificarea acreditărilor și nimic altceva. Să spunem că domeniul meu este „fun.xyz.tld”, calea OU este necunoscută, iar numele de utilizator/parola este testu/testp.

Știu că există câteva biblioteci Java care simplifică această sarcină, dar nu am reușit să le implementez. Majoritatea exemplelor pe care le-am găsit se adresează LDAP în general, nu în mod specific Active Directory. Emiterea unei cereri LDAP înseamnă trimiterea unei căi OU în ea, pe care eu nu o am. De asemenea, aplicația care emite cererea LDAP ar trebui să fie deja legată de Active Directory pentru a o accesa… Nesigur, deoarece acreditările ar trebui să fie stocate undeva unde pot fi descoperite. Aș dori un test bind cu acreditări de test, dacă este posibil – acest lucru ar însemna că contul este valid.

În sfârșit, dacă este posibil, există o modalitate de a face un astfel de mecanism de autentificare criptat? Știu că AD folosește Kerberos, dar nu sunt sigur că metodele LDAP din Java o fac.

Are cineva un exemplu de cod funcțional? Vă mulțumim.

9 răspunsuri
user8134

Există 3 protocoale de autentificare care pot fi utilizate pentru a efectua autentificarea între Java și Active Directory pe Linux sau pe orice altă platformă (și acestea nu sunt specifice doar serviciilor HTTP):

  1. Kerberos – Kerberos oferă Single Sign-On (SSO) și delegare, dar serverele web au nevoie, de asemenea, de suport SPNEGO pentru a accepta SSO prin IE.

  2. NTLM – NTLM acceptă SSO prin IE (și alte browsere dacă sunt configurate corespunzător).

  3. LDAP – Un bind LDAP poate fi utilizat pentru a valida pur și simplu un nume de cont și o parolă.

Există, de asemenea, ceva numit „ADFS” care oferă SSO pentru site-uri web folosind SAML care apelează la Windows SSP, astfel încât, în practică, este practic o modalitate indirectă de a folosi unul dintre celelalte protocoale de mai sus.

Fiecare protocol are avantajele sale, dar, ca regulă generală, pentru o compatibilitate maximă, ar trebui să încercați în general să „faceți ceea ce face Windows”. Deci, ce face Windows?

În primul rând, autentificarea între două mașini Windows favorizează Kerberos, deoarece serverele nu trebuie să comunice cu DC, iar clienții pot stoca în memoria cache biletele Kerberos, ceea ce reduce sarcina DC (și deoarece Kerberos acceptă delegarea).

Dar dacă părțile care se autentifică nu au amândouă conturi de domeniu sau dacă clientul nu poate comunica cu DC, este necesar NTLM. Așadar, Kerberos și NTLM nu se exclud reciproc, iar NTLM nu este depășit de Kerberos. De fapt, în unele privințe, NTLM este mai bun decât Kerberos. Rețineți că, atunci când menționez Kerberos și NTLM în același timp, trebuie să menționez și SPENGO și Autentificarea integrată a Windows (IWA). IWA este un termen simplu care înseamnă, în principiu, Kerberos sau NTLM sau SPNEGO pentru a negocia Kerberos sau NTLM.

Folosirea unui bind LDAP ca modalitate de validare a acreditărilor nu este eficientă și necesită SSL. Dar, până de curând, implementarea Kerberos și NTLM a fost dificilă, astfel încât utilizarea LDAP ca serviciu de autentificare improvizat a persistat. Dar, în acest moment, ar trebui în general evitată. LDAP este un director de informații și nu un serviciu de autentificare. Folosiți-l în scopul pentru care a fost creat.

Deci, cum se implementează Kerberos sau NTLM în Java și, în special, în contextul aplicațiilor web?

Există o serie de companii mari, precum Quest Software și Centrify, care au soluții care menționează în mod special Java. Nu pot să comentez cu adevărat despre acestea, deoarece sunt „soluții de gestionare a identității” la nivelul întregii companii, așa că, dacă ne uităm pe site-ul lor de marketing, este greu de spus exact ce protocoale sunt folosite și cum. Ar trebui să îi contactați pentru detalii.

Implementarea Kerberos în Java nu este extrem de dificilă, deoarece bibliotecile Java standard acceptă Kerberos prin clasele org.ietf.gssapi. Cu toate acestea, până de curând a existat un obstacol major – IE nu trimite token-uri Kerberos brute, ci token-uri SPNEGO. Dar odată cu Java 6, SPNEGO a fost implementat. Teoretic, ar trebui să puteți scrie un cod GSSAPI care să autentifice clienții IE. Dar nu am încercat. Implementarea Sun a Kerberos a fost o comedie a erorilor de-a lungul anilor, așa că, pe baza istoricului Sun în acest domeniu, nu aș face nicio promisiune cu privire la implementarea SPNEGO până când nu aveți pasărea în mână.

În ceea ce privește NTLM, există un proiect OSS gratuit numit JCIFS care are un filtru de servlet de autentificare NTLM HTTP. Cu toate acestea, acesta folosește o metodă de tip man-in-the-middle pentru a valida acreditările cu un server SMB care nu funcționează cu NTLMv2 (care devine încet-încet o politică de securitate obligatorie pentru domenii). Din acest motiv și din alte motive, partea de filtru HTTP din JCIFS este programată să fie eliminată. Rețineți că există o serie de produse derivate care utilizează JCIFS pentru a implementa aceeași tehnică. Prin urmare, dacă vedeți alte proiecte care pretind că acceptă NTLM SSO, verificați informațiile scrise cu litere mici.

Singura modalitate corectă de validare a acreditărilor NTLM cu Active Directory este utilizarea apelului DCERPC NetrLogonSamLogon prin NETLOGON cu Secure Channel. Există un astfel de lucru în Java? Da. Iată-l:

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

Jespa este o implementare NTLM 100% Java care suportă NTLMv2, NTLMv1, opțiuni complete de integritate și confidențialitate și validarea credențialelor NETLOGON menționată anterior. Și include un filtru HTTP SSO, un JAAS LoginModule, un client HTTP, un client și un server SASL (cu legătură JNDI), un „furnizor de securitate” generic pentru crearea de servicii NTLM personalizate și multe altele.

Mike

Comentarii

  • Actualizare: HttpClient suportă acum schemele de autentificare Basic, Digest, NTLMv1, NTLMv2, NTLM2 Session, SNPNEGO, Kerberos. –  > Por Jonathan Barbero.
DV.

Iată codul pe care l-am alcătuit pe baza exemplului de pe acest blog: LINK și această sursă: 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();
    }

}

Comentarii

  • import com.sun.jndi.ldap.LdapCtxFactory; – acest lucru va funcționa, cel mai probabil, doar cu un JVM Sun. –  > Por Thorbjørn Ravn Andersen.
Alexandru Luchian

Tocmai am terminat un proiect care folosește AD și Java. am folosit Spring ldapTemplate.

AD este compatibil cu LDAP (aproape), nu cred că veți avea probleme cu sarcina pe care o aveți. Mă refer la faptul că este AD sau orice alt server LDAP nu contează dacă vrei doar să te conectezi.

Eu aș arunca o privire la: Spring LDAP

Au și exemple.

În ceea ce privește criptarea, am folosit conexiunea SSL (deci era LDAPS). AD a trebuit să fie configurat pe un port/protocol SSL.

Dar, înainte de toate, asigurați-vă că vă puteți conecta corect la AD prin intermediul unui IDE LDAP. Eu folosesc Apache Directory Studio, , este foarte mișto și este scris în Java. Asta este tot ce aveam nevoie. În scopuri de testare, ați putea, de asemenea, să instalați Apache Directory Server

Comentarii

  • Luchiani, în prezent dezvolt o aplicație web pentru integrarea Java Spring în Share Point (Windows), dar nu reușesc să creez un utilizator în Active Directory folosind codul Java, poți să ne împărtășești codul tău pentru crearea unui utilizator în Active Directory cu un comentariu, astfel încât să îmi pot continua munca la timp. –  > Por Janak Dhanani.
Tommy McGuire

După cum au spus ioplex și alții, există multe opțiuni. Pentru a mă autentifica folosind LDAP (și Novell LDAP API), am folosit ceva de genul:


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

Ca o „caracteristică specială”, Active Directory permite legări LDAP față de „[email protected]” fără a utiliza numele distins al contului. Acest cod utilizează StartTLS pentru a activa criptarea TLS pe conexiune; cealaltă alternativă este LDAP prin SSL, care nu este suportată de către my serverele AD.

Adevărata șmecherie constă în localizarea serverului și a gazdei; modul oficial este de a utiliza o căutare a înregistrărilor DNS SRV (serviciu) pentru a localiza un pachet de gazde candidate, apoi de a efectua un „ping” LDAP pe bază de UDP (într-un anumit format Microsoft) pentru a localiza serverul corect. Dacă sunteți interesat, am postat câteva articole pe blog despre călătoria mea de aventură și descoperire în acest domeniu.

Dacă doriți să faceți o autentificare bazată pe nume de utilizator/parolă Kerberos, aveți de-a face cu o altă problemă; se poate face cu codul Java GSS-API, deși nu sunt sigur că acesta efectuează pasul final de validare a autentificării. (Codul care face validarea poate contacta serverul AD pentru a verifica numele de utilizator și parola, ceea ce are ca rezultat un bilet de acordare a unui bilet pentru utilizator, dar pentru a se asigura că serverul AD nu se face de fapt că se face că se face că se substituie, trebuie, de asemenea, să încerce să obțină el însuși un bilet pentru utilizator, ceea ce este ceva mai complicat).

Dacă doriți să realizați o autentificare unică bazată pe Kerberos, presupunând că utilizatorii sunt autentificați în domeniu, puteți face și acest lucru cu ajutorul codului Java GSS-API. Aș posta o mostră de cod, dar încă trebuie să transform prototipul meu hidos în ceva potrivit pentru ochii oamenilor. Consultați câteva coduri de la SpringSource pentru o oarecare inspirație.

Dacă sunteți în căutarea NTLM (despre care mi s-a dat de înțeles că este mai puțin sigur) sau altceva, ei bine, noroc.

Comentarii

  • Articole foarte utile pe blog. Vă mulțumesc! –  > Por Andrey Rodionov.
Anthony

Sunteți doar verificarea acreditărilor? În acest caz, ați putea face doar simplu kerberos și să nu vă deranjați cu LDAP.

Comentarii

  • Da, doar verificarea acreditărilor. Am editat întrebarea cu clarificări. Este codul diferit de LDAP auth? –  > Por DV..
Pat Gonzalez

Dacă tot ce doriți să faceți este să vă autentificați față de AD folosind Kerberos, atunci un simplu cod http://spnego.sourceforge.net/HelloKDC.java program simplu ar trebui să facă acest lucru.

Aruncați o privire la documentația „pre-flight” a proiectului, care vorbește despre programul HelloKDC.java.

Mash Vezi
Seema Kiran

Autentificarea ldap fără SSL nu este sigură și oricine poate vizualiza credențialele utilizatorului deoarece clientul ldap transferă numele de utilizator și parola în timpul operațiunii de legare ldap: Ldap authentication Active directory în Java Spring Security cu exemplu

Yair Zaslavsky

Vă recomand să vă uitați la pachetul adbroker al oVirt proiect. Acesta utilizează Spring-Ldap și modulul Krb5 JAAS Login (cu GSSAPI) pentru a se autentifica folosind Kerberos față de serverele Ldap (Active-Directory, ipa, rhds, Tivoli-DS). Căutați codul la enginebackendmanagermodulesbllsrcmainjavaorgovirtenginecoreblladbroker

Puteți folosi git pentru a clona depozitul sau puteți naviga folosind gerrit link