2012-09-07 285 views
17

我需要爲應用程序執行LDAP身份驗證。使用Java的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 Connection: 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.<init>(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) at javax.naming.InitialContext.init(InitialContext.java:308) at javax.naming.ldap.InitialLdapContext.<init>(InitialLdapContext.java:99) at LdapContextCreation.getLdapContext(LdapContextCreation.java:27) at LdapContextCreation.main(LdapContextCreation.java:12)

數點考慮:

  1. 早些時候,我使用tomcat 5.3.5但有人告訴我只有tomcat 6 sup端口它,所以我下載了tomcat 6.0.35,目前只使用這個版本。

  2. 配置server.xml並添加以下代碼 -

<Realm className="org.apache.catalina.realm.JNDIRealm" debug="99" connectionURL="ldap://server.domain.com:389/"
userPattern="{0}" />

  • 評論從server.xml以下代碼 -

    <!-- Commenting for LDAP <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> -->

  • 步驟2和步驟3從article

  • 有人建議有一些jar文件應該被複制到tomcat以運行ldap身份驗證,那我需要做些什麼?哪個jar文件?

  • 此外,我使用正確的憑據肯定,那麼是什麼原因造成這個問題?

  • 有沒有一種方法,我可以找出LDAP的正確屬性,以防我使用不正確的屬性?

  • +0

    這有更好的庫,但這裏是一個類似的問題http://stackoverflow.com/a/12165647/1286621我@jasim同意有關負責人。你需要弄清楚你的LDAP服務器使用什麼格式。以下是我的Active Directory服務器「CN = bindUserName,CN = Users,DC = myDepartment,DC = myNetwork」的示例。 LDAP人員應該很快就能告訴你格式是什麼。還有一些gui工具可以連接到LDAP並瀏覽目錄。首先與你的管理員交談。 – Mike

    +0

    還有一點意見,你知道通常有一個「綁定」用戶/密碼,一個有權在LDAP服務器中查找信息的權限正確嗎?一旦綁定到服務器,您就可以驗證用戶憑證。 – Mike

    回答

    6

    您必須提供整個用戶DN在SECURITY_PRINCIPAL

    這樣

     env.put(Context.SECURITY_PRINCIPAL, "cn=username,ou=testOu,o=test"); 
    
    +0

    不正確,這取決於服務器的實現。 –

    +1

    @ Michael-O當然這是真的。 (1)它在JNDI規範中。 (2)在所有LDAP服務器實現上,通過LDAP「綁定」操作完成LDAP認證。 (3)克服服務器實現中的差異是JNDI的功能。 – EJP

    +0

    @EJP,我正在討論只使用DN進行綁定的過程。 –

    16

    下面的代碼使用純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; 
    } 
    
    +0

    我知道這個條目有點舊,但我真的有這個灼熱的問題:爲什麼我們需要一個管理員用戶,爲什麼我們不能簡單地嘗試綁定我們想要首先驗證的用戶憑證? –

    11

    這是我的LDAP的Java登錄測試應用程序,支持LDAP://和LDAPS://自簽名的測試證書。代碼來自少數SO職位,簡化實施並刪除了傳統的sun.java。*進口。

    使用
    我已經對WinAD目錄服務的Windows7和Linux機器上運行這一點。應用程序打印用戶名和成員組。

    $ java命令類test.LoginLDAP URL = LDAP://1.2.3.4:389 [email protected] 密碼= MYPWD

    $ java命令類test.LoginLDAP URL = LDAPS://1.2.3.4:636 [email protected] 密碼= MYPWD

    測試應用程序支持臨時的自簽名的測試證書LDAPS://協議,這DummySSLFactory接受任何服務器證書使男人在中間是可能的。實際安裝應該將服務器證書導入本地JKS密鑰庫文件,而不是使用虛擬工廠。

    應用程序對初始上下文和ldap查詢使用enduser的用戶名+密碼,它適用於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]; 
        } 
    } 
    
    0

    //作者:Mirza Qasim Ali。

    //這個類將驗證LDAP用戶名和電子郵件

    //只需撥打LdapAuth.authenticateUserAndGetInfo(用戶名,密碼);

    //注:配置ldapURI,requiredAttributes,ADSearchPaths,accountSuffex

    進口的java.util。*;

    import javax.naming。*;

    import java.util.regex。*;

    import javax.naming.directory。*;

    import javax.naming.ldap.InitialLdapContext;

    import javax.naming.ldap。LdapContext的;

    公共類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"}; 
    

    //看你的Active Directory用戶OU的hirarchy

    私人靜態的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) { 
    
        } 
    } 
    

    }