web-dev-qa-db-fra.com

Authentification LDAP avec Java

Je dois effectuer une authentification LDAP pour une application.

J'ai essayé le programme suivant:

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;  
    }  

}

Obtention de l'exception suivante:

 Connexion LDAP: FAILED 
 Javax.naming.AuthenticationException: [LDAP: code d'erreur 49 - Informations d'identification non valides] 
 à l'adresse com.Sun.jndi.ldap.LdapCtx.mapErrorCode (LdapCtx.Java:3053) 
 à l'adresse com.Sun.jndi.ldap.LdapCtx.processReturnCode (LdapCtx.Java:2999) 
 à l'adresse com.Sun.jndi.ldap.LdapCtx.processReturnCode (LdapCtx.Java:2801) 
 à l'adresse com.Sun.jndi.ldap.LdapCtx.connect (LdapCtx.Java:2715) 
 sur com.Sun.jndi.ldap.LdapCtx. (LdapCtx.Java:305) 
 à l'adresse com.Sun.jndi.ldap.LdapCtxFactory.getUsingURL (LdapCtxFactory.Java:187) 
 à l'adresse com.Sun.jndi.ldap.LdapCtxFactory.getUsingURLs (LdapCtxFactory.Java:205) 
 à l'adresse com.Sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance (LdapCtxFactory.Java:148) 
 à l'adresse com.Sun.jndi.ldap.LdapCtxFactory.getInitialContext (LdapCtxFactory.Java:78) 
 à javax.naming.spi.NamingManager.getInitialContext (NamingManager.Java:235) 
 à l'adresse javax.naming.InitialContext.initializeDefaultInitCtx (InitialContext.Java:318) 
 sur javax.naming.InitialContext.getDefaultInitCtx (InitialContext.Java:348) 
 sur javax.naming.InitialContext.internalInit (InitialContext.Java:286) 
 sur javax.naming.InitialContext.init (InitialContext.Java:308) 
 sur javax.naming.ldap.InitialLdapContext. (InitialLdapContext.Java:99) 
 sur LdapContextCreation.getLdapContext (LdapContextCreation.Java:27) 
 à LdapContextCreation.main (LdapContextCreation.Java:12) 

Quelques points à prendre en compte:

  1. Auparavant, j'utilisais Tomcat 5.3.5 mais quelqu'un m'a dit que seul Tomcat 6 le prend en charge. J'ai donc téléchargé Tomcat 6.0.35 et n'utilise actuellement que cette version.

  2. server.xml configuré et ajouté le code suivant -

    <Realm className="org.Apache.catalina.realm.JNDIRealm" 
                       debug="99" 
                       connectionURL="ldap://server.domain.com:389/"  
                       userPattern="{0}" />
    
  3. A commenté le code suivant de server.xml -

    <!-- Commenting for LDAP
      <Realm className="org.Apache.catalina.realm.UserDatabaseRealm"
         resourceName="UserDatabase"/> -->
    
  4. Etapes 2 et 3 de article

  5. Quelqu'un a suggéré que certains fichiers JAR sont supposés être copiés sur Tomcat afin d'exécuter l'authentification ldap. Est-ce une chose à faire? Et quels fichiers jar?

  6. En outre, j'utilise bien les informations d'identification correctes, alors qu'est-ce qui cause ce problème?

  7. Existe-t-il un moyen de déterminer les attributs corrects pour LDAP au cas où j'utiliserais des attributs incorrects?

24
anujin

Le code suivant s'authentifie à partir de LDAP à l'aide de JNDI Java pur. Le principe est: -

  1. D'abord, recherchez l'utilisateur à l'aide d'un utilisateur administrateur ou DN. 
  2. L'objet utilisateur doit être à nouveau transmis à LDAP avec les informations d'identification de l'utilisateur. 
  3. Aucune exception signifie - authentifié avec succès. Sinon, l'authentification a échoué.

Extrait de code

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

Ceci est mon application de test de connexion Java LDAP prenant en charge les certificats LDAP: // et LDAPS: // auto-signé. Le code provient de quelques SO publications, d'une implémentation simplifiée et de la suppression des importations Sun.Java. * Héritées.

Utilisation
Je l'ai exécuté sur des machines Windows7 et Linux avec le service d'annuaire WinAD. L'application imprime le nom d'utilisateur et les groupes de membres.

$ Java -cp classes test.LoginLDAP url = ldap: //1.2.3.4: 389 nom_utilisateur = pré[email protected] mot de passe = mypwd

$ Java -cp classes test.LoginLDAP url = ldaps: //1.2.3.4: 636 nom_utilisateur = pré[email protected] mot de passe = mypwd

L’application de test prend en charge les certificats de test auto-signés temporaires pour le protocole ldaps: //, ce DummySSLFactory accepte n’importe quel certificat de serveur, de sorte que l’intermédiaire est possible Une installation réelle doit importer le certificat du serveur dans un fichier de clés JKS local et ne pas utiliser de fabrique factice.

L'application utilise le nom d'utilisateur et le mot de passe de l'utilisateur final pour les requêtes de contexte et LDAP initiales. Cela fonctionne pour WinAD mais ne sait pas si peut être utilisé pour toutes les implémentations de serveur LDAP. Vous pouvez créer un contexte avec un nom d'utilisateur interne + pwd, puis exécuter des requêtes pour voir si un utilisateur donné est trouvé.

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;
    }

}

Et la classe d'assistance SSL.

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

Vous devrez fournir l'intégralité de l'utilisateur dn dans SECURITY_PRINCIPAL

comme ça

     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