wake-up-neo.com

LDAP-Authentifizierung mit Java

Ich muss die LDAP-Authentifizierung für eine Anwendung durchführen.

Ich habe folgendes Programm ausprobiert:

import Java.util.Hashtable;  

import javax.naming.Context;  
import javax.naming.NamingException;  
import javax.naming.ldap.InitialLdapContext;  
import javax.naming.ldap.LdapContext;  


public class LdapContextCreation {  
    public static void main(String[] args) {  
        LdapContextCreation ldapContxCrtn = new LdapContextCreation();  
        LdapContext ctx = ldapContxCrtn.getLdapContext();  
    }  
    public LdapContext getLdapContext(){  
        LdapContext ctx = null;  
        try{  
            Hashtable env = new Hashtable();  
            env.put(Context.INITIAL_CONTEXT_FACTORY,  "com.Sun.jndi.ldap.LdapCtxFactory");  
            env.put(Context.SECURITY_AUTHENTICATION, "Simple");  
            //it can be <domain\\userid> something that you use for windows login  
            //it can also be  
            env.put(Context.SECURITY_PRINCIPAL, "[email protected]");  
            env.put(Context.SECURITY_CREDENTIALS, "password");  
            //in following property we specify ldap protocol and connection url.  
            //generally the port is 389  
            env.put(Context.PROVIDER_URL, "ldap://server.domain.com");  
            ctx = new InitialLdapContext(env, null);  
            System.out.println("Connection Successful.");  
        }catch(NamingException nex){  
            System.out.println("LDAP Connection: FAILED");  
            nex.printStackTrace();  
        }  
        return ctx;  
    }  

}

Folgende Ausnahme erhalten:

 LDAP-Verbindung: FAILED 
 Javax.naming.AuthenticationException: [LDAP: Fehlercode 49 - Ungültige Anmeldeinformationen] 
 at com.Sun.jndi.ldap.LdapCtx.mapErrorCode (LdapCtx.Java:3053) 
 at com.Sun.jndi.ldap.LdapCtx.processReturnCode (LdapCtx.Java:2999) 
 at com.Sun.jndi.ldap.LdapCtx.processReturnCode (LdapCtx.Java:2801) 
 unter com.Sun.jndi.ldap.LdapCtx.connect (LdapCtx.Java:2715) 
 at com.Sun.jndi.ldap.LdapCtx (LdapCtx.Java:305) 
 at com.Sun.jndi.ldap.LdapCtxFactory.getUsingURL (LdapCtxFactory.Java:187) 
 at com.Sun.jndi.ldap.LdapCtxFactory.getUsingURLs (LdapCtxFactory.Java:205) 
 at com.Sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance (LdapCtxFactory.Java:148) 
 at com.Sun.jndi.ldap.LdapCtxFactory.getInitialContext (LdapCtxFactory.Java:78) 
 at javax.naming.spi.NamingManager.getInitialContext (NamingManager.Java:235) 
 at javax.naming.InitialContext.initializeDefaultInitCtx (InitialContext.Java:318) 
 at javax.naming.InitialContext.getDefaultInitCtx (InitialContext.Java:348) 
 at javax.naming.InitialContext.internalInit (InitialContext.Java:286) 
 bei javax.naming.InitialContext.init (InitialContext.Java:308) 
 at javax.naming.ldap.InitialLdapContext. (InitialLdapContext.Java:99) 
 at LdapContextCreation.getLdapContext (LdapContextCreation.Java:27) 
 at LdapContextCreation.main (LdapContextCreation.Java:12) 

Einige weitere Punkte zu berücksichtigen:

  1. Früher habe ich Tomcat 5.3.5 verwendet, aber jemand hat mir gesagt, dass nur Tomcat 6 dies unterstützt, also habe ich Tomcat 6.0.35 heruntergeladen und derzeit nur diese Version.

  2. Konfigurierte server.xml und fügte den folgenden Code hinzu -

    <Realm className="org.Apache.catalina.realm.JNDIRealm" 
                       debug="99" 
                       connectionURL="ldap://server.domain.com:389/"  
                       userPattern="{0}" />
    
  3. Kommentierte den folgenden Code von server.xml -

    <!-- Commenting for LDAP
      <Realm className="org.Apache.catalina.realm.UserDatabaseRealm"
         resourceName="UserDatabase"/> -->
    
  4. Schritte 2 und 3 aus Artikel

  5. Jemand schlug vor, dass es einige JAR-Dateien gibt, die nach Tomcat kopiert werden sollten, um die ldap-Authentifizierung auszuführen. Muss ich das tun? Und welche jar-Dateien?

  6. Außerdem verwende ich sicher die korrekten Anmeldeinformationen. Was verursacht dann dieses Problem?

  7. Gibt es eine Möglichkeit, die korrekten Attribute für LDAP herauszufinden, falls ich falsche Werte verwende?

24
anujin

Der folgende Code authentifiziert sich aus LDAP mit reinem Java JNDI. Das Prinzip ist: -

  1. Suchen Sie den Benutzer zuerst mit einem Admin- oder DN-Benutzer. 
  2. Das Benutzerobjekt muss erneut mit den Benutzeranmeldeinformationen an LDAP übergeben werden 
  3. Keine Ausnahme bedeutet - Erfolgreich authentifiziert. Sonst Authentifizierung fehlgeschlagen.

Code-Auszug

public static boolean authenticateJndi(String username, String password) throws Exception{
    Properties props = new Properties();
    props.put(Context.INITIAL_CONTEXT_FACTORY, "com.Sun.jndi.ldap.LdapCtxFactory");
    props.put(Context.PROVIDER_URL, "ldap://LDAPSERVER:PORT");
    props.put(Context.SECURITY_PRINCIPAL, "uid=adminuser,ou=special users,o=xx.com");//adminuser - User with special priviledge, dn user
    props.put(Context.SECURITY_CREDENTIALS, "adminpassword");//dn user password


    InitialDirContext context = new InitialDirContext(props);

    SearchControls ctrls = new SearchControls();
    ctrls.setReturningAttributes(new String[] { "givenName", "sn","memberOf" });
    ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);

    NamingEnumeration<javax.naming.directory.SearchResult> answers = context.search("o=xx.com", "(uid=" + username + ")", ctrls);
    javax.naming.directory.SearchResult result = answers.nextElement();

    String user = result.getNameInNamespace();

    try {
        props = new Properties();
        props.put(Context.INITIAL_CONTEXT_FACTORY, "com.Sun.jndi.ldap.LdapCtxFactory");
        props.put(Context.PROVIDER_URL, "ldap://LDAPSERVER:PORT");
        props.put(Context.SECURITY_PRINCIPAL, user);
        props.put(Context.SECURITY_CREDENTIALS, password);

   context = new InitialDirContext(props);
    } catch (Exception e) {
        return false;
    }
    return true;
}
19
Atanu Sarkar

Dies ist meine LDAP-Java-Login-Testanwendung, die LDAP: // und LDAPS: // selbstsigniertes Testzertifikat unterstützt. Der Code stammt aus wenigen SO - Posts, vereinfachte die Implementierung und entfernte ältere Sun.Java. * - Importe.

Verwendungszweck
Ich habe dies auf Windows7- und Linux-Computern für den WinAD-Verzeichnisdienst ausgeführt. Anwendung druckt Benutzernamen und Mitgliedsgruppen.

$ Java -cp classes test.LoginLDAP url = ldap: //1.2.3.4: 389 [email protected] Passwort = mypwd

$ Java -cp classes test.LoginLDAP url = ldaps: //1.2.3.4: 636 [email protected] Passwort = mypwd

Die Testanwendung unterstützt temporäre selbstsignierte Testzertifikate für das ldaps: // -Protokoll. Diese DummySSLFactory akzeptiert jedes Serverzertifikat, sodass Man-in-the-Middle möglich ist. Bei einer realen Installation sollte das Serverzertifikat in eine lokale JKS-Keystore-Datei importiert werden und keine Dummy-Factory verwendet werden.

Die Anwendung verwendet den Benutzernamen + das Kennwort des Endbenutzers für anfängliche Kontext- und LDAP-Abfragen. Es funktioniert für WinAD, weiß jedoch nicht, ob es für alle LDAP-Server-Implementierungen verwendet werden kann. Sie könnten einen Kontext mit dem internen Benutzernamen + pwd erstellen und dann Abfragen ausführen, um festzustellen, ob der angegebene Endbenutzer gefunden wurde.

LoginLDAP.Java

package test;

import Java.util.*;
import javax.naming.*;
import javax.naming.directory.*;

public class LoginLDAP {

    public static void main(String[] args) throws Exception {
        Map<String,String> params = createParams(args);

        String url = params.get("url"); // ldap://1.2.3.4:389 or ldaps://1.2.3.4:636
        String principalName = params.get("username"); // [email protected]
        String domainName = params.get("domain"); // mydomain.com or empty

        if (domainName==null || "".equals(domainName)) {
            int delim = principalName.indexOf('@');
            domainName = principalName.substring(delim+1);
        }

        Properties props = new Properties();
        props.put(Context.INITIAL_CONTEXT_FACTORY, "com.Sun.jndi.ldap.LdapCtxFactory");
        props.put(Context.PROVIDER_URL, url); 
        props.put(Context.SECURITY_PRINCIPAL, principalName); 
        props.put(Context.SECURITY_CREDENTIALS, params.get("password")); // secretpwd
        if (url.toUpperCase().startsWith("LDAPS://")) {
            props.put(Context.SECURITY_PROTOCOL, "ssl");
            props.put(Context.SECURITY_AUTHENTICATION, "simple");
            props.put("Java.naming.ldap.factory.socket", "test.DummySSLSocketFactory");         
        }

        InitialDirContext context = new InitialDirContext(props);
        try {
            SearchControls ctrls = new SearchControls();
            ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            NamingEnumeration<SearchResult> results = context.search(toDC(domainName),"(& (userPrincipalName="+principalName+")(objectClass=user))", ctrls);
            if(!results.hasMore())
                throw new AuthenticationException("Principal name not found");

            SearchResult result = results.next();
            System.out.println("distinguisedName: " + result.getNameInNamespace() ); // CN=Firstname Lastname,OU=Mycity,DC=mydomain,DC=com

            Attribute memberOf = result.getAttributes().get("memberOf");
            if(memberOf!=null) {
                for(int idx=0; idx<memberOf.size(); idx++) {
                    System.out.println("memberOf: " + memberOf.get(idx).toString() ); // CN=Mygroup,CN=Users,DC=mydomain,DC=com
                    //Attribute att = context.getAttributes(memberOf.get(idx).toString(), new String[]{"CN"}).get("CN");
                    //System.out.println( att.get().toString() ); //  CN part of groupname
                }
            }
        } finally {
            try { context.close(); } catch(Exception ex) { }
        }       
    }

    /**
     * Create "DC=sub,DC=mydomain,DC=com" string
     * @param domainName    sub.mydomain.com
     * @return
     */
    private static String toDC(String domainName) {
        StringBuilder buf = new StringBuilder();
        for (String token : domainName.split("\\.")) {
            if(token.length()==0) continue;
            if(buf.length()>0)  buf.append(",");
            buf.append("DC=").append(token);
        }
        return buf.toString();
    }

    private static Map<String,String> createParams(String[] args) {
        Map<String,String> params = new HashMap<String,String>();  
        for(String str : args) {
            int delim = str.indexOf('=');
            if (delim>0) params.put(str.substring(0, delim).trim(), str.substring(delim+1).trim());
            else if (delim==0) params.put("", str.substring(1).trim());
            else params.put(str, null);
        }
        return params;
    }

}

Und SSL-Helferklasse.

package test;

import Java.io.*;
import Java.net.*;
import Java.security.SecureRandom;
import Java.security.cert.X509Certificate;    
import javax.net.*;
import javax.net.ssl.*;

public class DummySSLSocketFactory extends SSLSocketFactory {
    private SSLSocketFactory socketFactory;
    public DummySSLSocketFactory() {
        try {
          SSLContext ctx = SSLContext.getInstance("TLS");
          ctx.init(null, new TrustManager[]{ new DummyTrustManager()}, new SecureRandom());
          socketFactory = ctx.getSocketFactory();
        } catch ( Exception ex ){ throw new IllegalArgumentException(ex); }
    }

      public static SocketFactory getDefault() { return new DummySSLSocketFactory(); }

      @Override public String[] getDefaultCipherSuites() { return socketFactory.getDefaultCipherSuites(); }
      @Override public String[] getSupportedCipherSuites() { return socketFactory.getSupportedCipherSuites(); }

      @Override public Socket createSocket(Socket socket, String string, int i, boolean bln) throws IOException {
        return socketFactory.createSocket(socket, string, i, bln);
      }
      @Override public Socket createSocket(String string, int i) throws IOException, UnknownHostException {
        return socketFactory.createSocket(string, i);
      }
      @Override public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException, UnknownHostException {
        return socketFactory.createSocket(string, i, ia, i1);
      }
      @Override public Socket createSocket(InetAddress ia, int i) throws IOException {
        return socketFactory.createSocket(ia, i);
      }
      @Override public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException {
        return socketFactory.createSocket(ia, i, ia1, i1);
      }
}

class DummyTrustManager implements X509TrustManager {
    @Override public void checkClientTrusted(X509Certificate[] xcs, String str) {
        // do nothing
    }
    @Override public void checkServerTrusted(X509Certificate[] xcs, String str) {
        /*System.out.println("checkServerTrusted for authType: " + str); // RSA
        for(int idx=0; idx<xcs.length; idx++) {
            X509Certificate cert = xcs[idx];
            System.out.println("X500Principal: " + cert.getSubjectX500Principal().getName());
        }*/
    }
    @Override public X509Certificate[] getAcceptedIssuers() {
        return new Java.security.cert.X509Certificate[0];
    }
}
16
Whome

Sie müssen den gesamten Benutzer dn in SECURITY_PRINCIPAL angeben

so was

     env.put(Context.SECURITY_PRINCIPAL, "cn=username,ou=testOu,o=test"); 
6
Andromeda
// this class will authenticate LDAP UserName or Email

// simply call LdapAuth.authenticateUserAndGetInfo (username,password);

//Note: Configure ldapURI ,requiredAttributes ,ADSearchPaths,accountSuffex 

import Java.util.*;

import javax.naming.*;

import Java.util.regex.*;

import javax.naming.directory.*;

import javax.naming.ldap.InitialLdapContext;

import javax.naming.ldap.LdapContext;

public class LdapAuth {


private final static String ldapURI = "ldap://20.200.200.200:389/DC=corp,DC=local";

private final static String contextFactory = "com.Sun.jndi.ldap.LdapCtxFactory";

private  static String[] requiredAttributes = {"cn","givenName","sn","displayName","userPrincipalName","sAMAccountName","objectSid","userAccountControl"};


// see you Active Directory user OU's hirarchy 

private  static String[] ADSearchPaths = 

{ 

    "OU=O365 Synced Accounts,OU=ALL USERS",

    "OU=Users,OU=O365 Synced Accounts,OU=ALL USERS",

    "OU=In-House,OU=Users,OU=O365 Synced Accounts,OU=ALL USERS",

    "OU=Torbram Users,OU=Users,OU=O365 Synced Accounts,OU=ALL USERS",

    "OU=Migrated Users,OU=TES-Users"

};


private static String accountSuffex = "@corp.local"; // this will be used if user name is just provided


private static void authenticateUserAndGetInfo (String user, String password) throws Exception {

    try {


        Hashtable<String,String> env = new Hashtable <String,String>();

        env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory);

        env.put(Context.PROVIDER_URL, ldapURI);     

        env.put(Context.SECURITY_AUTHENTICATION, "simple");

        env.put(Context.SECURITY_PRINCIPAL, user);

        env.put(Context.SECURITY_CREDENTIALS, password);

        DirContext ctx = new InitialDirContext(env);

        String filter = "(sAMAccountName="+user+")";  // default for search filter username

        if(user.contains("@"))  // if user name is a email then
        {
            //String parts[] = user.split("\\@");
            //use different filter for email
            filter = "(userPrincipalName="+user+")";
        }

        SearchControls ctrl = new SearchControls();
        ctrl.setSearchScope(SearchControls.SUBTREE_SCOPE);
        ctrl.setReturningAttributes(requiredAttributes);

        NamingEnumeration userInfo = null;


        Integer i = 0;
        do
        {
            userInfo = ctx.search(ADSearchPaths[i], filter, ctrl);
            i++;

        } while(!userInfo.hasMore() && i < ADSearchPaths.length );

        if (userInfo.hasMore()) {

            SearchResult UserDetails = (SearchResult) userInfo.next();
            Attributes userAttr = UserDetails.getAttributes();System.out.println("adEmail = "+userAttr.get("userPrincipalName").get(0).toString());

            System.out.println("adFirstName = "+userAttr.get("givenName").get(0).toString());

            System.out.println("adLastName = "+userAttr.get("sn").get(0).toString());

            System.out.println("name = "+userAttr.get("cn").get(0).toString());

            System.out.println("AdFullName = "+userAttr.get("cn").get(0).toString());

        }

        userInfo.close();

    }
    catch (javax.naming.AuthenticationException e) {

    }
}   
}
0
Qasim Mirza