web-dev-qa-db-ja.com

Javaを使用したLDAP認証

アプリケーションのLDAP認証を行う必要があります。

私は次のプログラムを試しました:

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

}

次の例外を取得:

 LDAP接続:FAILED 
 javax.naming.AuthenticationException:[LDAP:error code 49-Invalid Credentials] 
 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)
 at com.Sun.jndi.ldap.LdapCtx.connect(LdapCtx.Java:2715)
 at com.Sun.jndi.ldap.LdapCtx。(LdapCtx.Java:305)
 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)
 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)
 at javax.naming。 InitialContext.init(InitialContext.Java:308)
 javax.naming.ldap.InitialLdapContext。(InitialLdapContext.Java:99)
 at LdapContextCreation.getLdapContext(LdapContextCreation.Java:27)
 LdapContextCreation.main(LdapContextCreation.Java:12)

考慮すべき点はまだありません:

  1. 以前はTomcat 5.3.5を使用していましたが、Tomcat 6のみがサポートしていると誰かに言われたので、Tomcat 6.0.35をダウンロードし、現在このバージョンのみを使用しています。

  2. server.xmlを構成し、次のコードを追加しました-

    <Realm className="org.Apache.catalina.realm.JNDIRealm" 
                       debug="99" 
                       connectionURL="ldap://server.domain.com:389/"  
                       userPattern="{0}" />
    
  3. server.xmlの次のコードにコメントしました-

    <!-- Commenting for LDAP
      <Realm className="org.Apache.catalina.realm.UserDatabaseRealm"
         resourceName="UserDatabase"/> -->
    
  4. 記事 のステップ2と3

  5. 誰かがldap認証を実行するためにTomcatにコピーされることになっているいくつかのjarファイルがあることを示唆しましたが、それは私がする必要があることですか?そして、どのjarファイル?

  6. また、正しい資格情報を確実に使用していますが、この問題の原因は何ですか?

  7. 間違った属性を使用している場合に、LDAPの正しい属性を把握する方法はありますか?

29
anujin

以下のコードは、純粋なJava JNDIを使用してLDAPから認証します。原理は次のとおりです。

  1. まず、管理者またはDNユーザーを使用してユーザーを検索します。
  2. ユーザーオブジェクトは、ユーザー資格情報を使用してLDAPに再度渡す必要があります。
  3. 例外は意味しません-認証に成功しました。それ以外の認証は失敗しました。

コードスニペット

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;
}
22
Atanu Sarkar

これは私のLDAP Java LDAP://およびLDAPS://自己署名テスト証明書をサポートするログインテストアプリケーションです。コードはいくつかのSO投稿、実装を簡素化し、従来のSun.Java。*インポートを削除しました。

使用法
Windows7およびLinuxマシンでWinADディレクトリサービスに対してこれを実行しました。アプリケーションは、ユーザー名とメンバーグループを出力します。

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

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

テストアプリケーションは、ldaps://プロトコルの一時的な自己署名テスト証明書をサポートします。このDummySSLFactoryは、任意のサーバー証明書を受け入れるため、中間者が可能です。実際のインストールでは、サーバー証明書をローカルのJKSキーストアファイルにインポートし、ダミーファクトリを使用しないでください。

アプリケーションは、初期コンテキストとLDAPクエリにエンドユーザーのユーザー名とパスワードを使用します。WinADには機能しますが、すべてのLDAPサーバー実装に使用できるかどうかはわかりません。内部のユーザー名+ pwdでコンテキストを作成し、クエリを実行して、特定のエンドユーザーが見つかったかどうかを確認できます。

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

}

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];
    }
}
20
Whome

SECURITY_PRINCIPALのユーザー全体dnを提供する必要があります

このような

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