2009-02-20 13 views
23

Accediamo agli utenti in Active Directory tramite LDAP utilizzando l'API LDAP Java. Vogliamo migliorare la nostra funzionalità di accesso per verificare ulteriormente se l'utente si trova in un determinato gruppo di annunci. Qualcuno sa come fare questo?LDAP Java - Determina se l'utente in un determinato gruppo?

codice attuale:

import javax.naming.*; 
import javax.naming.ldap.*; 

LdapContext ctx = null; 
Hashtable env = new Hashtable(); 
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory"); 
env.put(Context.SECURITY_AUTHENTICATION,"simple"); 
env.put(Context.PROVIDER_URL, Config.get("ldap-url")); 

try { 
    Control[] connCtls = new Control[] {new FastBindConnectionControl()}; 
    ctx = new InitialLdapContext(env, connCtls); 
    ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, "DOMAIN\\" + username); 
    ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password); 
    ctx.reconnect(connCtls); 
    /* TODO: Only return true if user is in group "ABC" */ 
    return true; //User authenticated 
} catch (Exception e) { 
    return false; //User could NOT be authenticated 
} finally { 
    ... 
} 

Aggiornamento: Vedere la soluzione qui di seguito.

risposta

18

Abbiamo risolto questo problema con la classe seguente. Basta chiamare il metodo autenticazione:

import java.text.MessageFormat; 
import java.util.*;  
import javax.naming.*;  
import org.apache.log4j.Level; 

public class LdapGroupAuthenticator { 
    public static final String DISTINGUISHED_NAME = "distinguishedName"; 
    public static final String CN = "cn"; 
    public static final String MEMBER = "member"; 
    public static final String MEMBER_OF = "memberOf"; 
    public static final String SEARCH_BY_SAM_ACCOUNT_NAME = "(SAMAccountName={0})"; 
    public static final String SEARCH_GROUP_BY_GROUP_CN = "(&(objectCategory=group)(cn={0}))"; 

    /* 
    * Prepares and returns CN that can be used for AD query 
    * e.g. Converts "CN=**Dev - Test Group" to "**Dev - Test Group" 
    * Converts CN=**Dev - Test Group,OU=Distribution Lists,DC=DOMAIN,DC=com to "**Dev - Test Group" 
    */ 
    public static String getCN(String cnName) { 
     if (cnName != null && cnName.toUpperCase().startsWith("CN=")) { 
      cnName = cnName.substring(3); 
     } 
     int position = cnName.indexOf(','); 
     if (position == -1) { 
      return cnName; 
     } else { 
      return cnName.substring(0, position); 
     } 
    } 
    public static boolean isSame(String target, String candidate) { 
     if (target != null && target.equalsIgnoreCase(candidate)) { 
      return true; 
     } 
     return false; 
    } 

    public static boolean authenticate(String domain, String username, String password) { 
     Hashtable env = new Hashtable(); 
     env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory"); 
     env.put(Context.PROVIDER_URL, "ldap://1.2.3.4:389"); 
     env.put(Context.SECURITY_AUTHENTICATION, "simple"); 
     env.put(Context.SECURITY_PRINCIPAL, domain + "\\" + username); 
     env.put(Context.SECURITY_CREDENTIALS, password); 
     DirContext ctx = null; 
     String defaultSearchBase = "DC=DOMAIN,DC=com"; 
     String groupDistinguishedName = "DN=CN=DLS-APP-MyAdmin-C,OU=DLS File Permissions,DC=DOMAIN,DC=com"; 

     try { 
      ctx = new InitialDirContext(env); 

      // userName is SAMAccountName 
      SearchResult sr = executeSearchSingleResult(ctx, SearchControls.SUBTREE_SCOPE, defaultSearchBase, 
        MessageFormat.format(SEARCH_BY_SAM_ACCOUNT_NAME, new Object[] {username}), 
        new String[] {DISTINGUISHED_NAME, CN, MEMBER_OF} 
        ); 

      String groupCN = getCN(groupDistinguishedName); 
      HashMap processedUserGroups = new HashMap(); 
      HashMap unProcessedUserGroups = new HashMap(); 

      // Look for and process memberOf 
      Attribute memberOf = sr.getAttributes().get(MEMBER_OF); 
      if (memberOf != null) { 
       for (Enumeration e1 = memberOf.getAll() ; e1.hasMoreElements() ;) { 
        String unprocessedGroupDN = e1.nextElement().toString(); 
        String unprocessedGroupCN = getCN(unprocessedGroupDN); 
        // Quick check for direct membership 
        if (isSame (groupCN, unprocessedGroupCN) && isSame (groupDistinguishedName, unprocessedGroupDN)) { 
         Log.info(username + " is authorized."); 
         return true; 
        } else { 
         unProcessedUserGroups.put(unprocessedGroupDN, unprocessedGroupCN); 
        } 
       } 
       if (userMemberOf(ctx, defaultSearchBase, processedUserGroups, unProcessedUserGroups, groupCN, groupDistinguishedName)) { 
        Log.info(username + " is authorized."); 
        return true; 
       } 
      } 

      Log.info(username + " is NOT authorized."); 
      return false; 
     } catch (AuthenticationException e) { 
      Log.info(username + " is NOT authenticated"); 
      return false; 
     } catch (NamingException e) { 
      throw new SystemException(e); 
     } finally { 
      if (ctx != null) { 
       try { 
        ctx.close(); 
       } catch (NamingException e) { 
        throw new SystemException(e); 
       } 
      } 
     } 
    } 

    public static boolean userMemberOf(DirContext ctx, String searchBase, HashMap processedUserGroups, HashMap unProcessedUserGroups, String groupCN, String groupDistinguishedName) throws NamingException { 
     HashMap newUnProcessedGroups = new HashMap(); 
     for (Iterator entry = unProcessedUserGroups.keySet().iterator(); entry.hasNext();) { 
      String unprocessedGroupDistinguishedName = (String) entry.next(); 
      String unprocessedGroupCN = (String)unProcessedUserGroups.get(unprocessedGroupDistinguishedName); 
      if (processedUserGroups.get(unprocessedGroupDistinguishedName) != null) { 
       Log.info("Found : " + unprocessedGroupDistinguishedName +" in processedGroups. skipping further processing of it..."); 
       // We already traversed this. 
       continue; 
      } 
      if (isSame (groupCN, unprocessedGroupCN) && isSame (groupDistinguishedName, unprocessedGroupDistinguishedName)) { 
       Log.info("Found Match DistinguishedName : " + unprocessedGroupDistinguishedName +", CN : " + unprocessedGroupCN); 
       return true; 
      } 
     } 

     for (Iterator entry = unProcessedUserGroups.keySet().iterator(); entry.hasNext();) { 
      String unprocessedGroupDistinguishedName = (String) entry.next(); 
      String unprocessedGroupCN = (String)unProcessedUserGroups.get(unprocessedGroupDistinguishedName); 

      processedUserGroups.put(unprocessedGroupDistinguishedName, unprocessedGroupCN); 

      // Fetch Groups in unprocessedGroupCN and put them in newUnProcessedGroups 
      NamingEnumeration ns = executeSearch(ctx, SearchControls.SUBTREE_SCOPE, searchBase, 
        MessageFormat.format(SEARCH_GROUP_BY_GROUP_CN, new Object[] {unprocessedGroupCN}), 
        new String[] {CN, DISTINGUISHED_NAME, MEMBER_OF}); 

      // Loop through the search results 
      while (ns.hasMoreElements()) { 
       SearchResult sr = (SearchResult) ns.next(); 

       // Make sure we're looking at correct distinguishedName, because we're querying by CN 
       String userDistinguishedName = sr.getAttributes().get(DISTINGUISHED_NAME).get().toString(); 
       if (!isSame(unprocessedGroupDistinguishedName, userDistinguishedName)) { 
        Log.info("Processing CN : " + unprocessedGroupCN + ", DN : " + unprocessedGroupDistinguishedName +", Got DN : " + userDistinguishedName +", Ignoring..."); 
        continue; 
       } 

       Log.info("Processing for memberOf CN : " + unprocessedGroupCN + ", DN : " + unprocessedGroupDistinguishedName); 
       // Look for and process memberOf 
       Attribute memberOf = sr.getAttributes().get(MEMBER_OF); 
       if (memberOf != null) { 
        for (Enumeration e1 = memberOf.getAll() ; e1.hasMoreElements() ;) { 
         String unprocessedChildGroupDN = e1.nextElement().toString(); 
         String unprocessedChildGroupCN = getCN(unprocessedChildGroupDN); 
         Log.info("Adding to List of un-processed groups : " + unprocessedChildGroupDN +", CN : " + unprocessedChildGroupCN); 
         newUnProcessedGroups.put(unprocessedChildGroupDN, unprocessedChildGroupCN); 
        } 
       } 
      } 
     } 
     if (newUnProcessedGroups.size() == 0) { 
      Log.info("newUnProcessedGroups.size() is 0. returning false..."); 
      return false; 
     } 

     // process unProcessedUserGroups 
     return userMemberOf(ctx, searchBase, processedUserGroups, newUnProcessedGroups, groupCN, groupDistinguishedName); 
    } 

    private static NamingEnumeration executeSearch(DirContext ctx, int searchScope, String searchBase, String searchFilter, String[] attributes) throws NamingException { 
     // Create the search controls 
     SearchControls searchCtls = new SearchControls(); 

     // Specify the attributes to return 
     if (attributes != null) { 
      searchCtls.setReturningAttributes(attributes); 
     } 

     // Specify the search scope 
     searchCtls.setSearchScope(searchScope); 

     // Search for objects using the filter 
     NamingEnumeration result = ctx.search(searchBase, searchFilter,searchCtls); 
     return result; 
    } 

    private static SearchResult executeSearchSingleResult(DirContext ctx, int searchScope, String searchBase, String searchFilter, String[] attributes) throws NamingException { 
     NamingEnumeration result = executeSearch(ctx, searchScope, searchBase, searchFilter, attributes); 

     SearchResult sr = null; 
     // Loop through the search results 
     while (result.hasMoreElements()) { 
      sr = (SearchResult) result.next(); 
      break; 
     } 
     return sr; 
    } 
} 
+3

Nota: questo non è del tutto completo perché i gruppi possono essere nidificati in Active Directory, quindi non si otterranno utenti che sono membri di un gruppo in quanto membri di un altro gruppo. Il modo giusto per farlo è quello di ottenere l'attributo tokenGroups dell'utente che contiene i gruppi come un elenco di SID, tuttavia questo è un attributo binario che deve essere decodificato. Vedi: http://blogs.msdn.com/alextch/archive/2007/06/18/sample-java-application-that-retrieves-group-membership-of-an-active-directory-user-account.aspx –

1

Sfortunatamente la risposta varia con le installazioni di AD così come con altri tipi di server LDAP.

Abbiamo dovuto risolvere lo stesso problema. Alla fine abbiamo permesso all'amministratore di sistema di fornirci un modello di query LDAP in cui sostituiamo il nome utente (e il nome del gruppo, se necessario, anche nel modello).

+0

Grazie. Perché la risposta dovrebbe variare? Sembra essere una query abbastanza standard. Ti capita di avere un esempio di codice che utilizza la tua query LDAP? –

+0

Sospetto che varia in quale versione di Active Directory a causa di come AD gestisce memberOf. Memorizza l'appartenenza al gruppo sul gruppo e non sull'oggetto utente. L'attributo memberOf sull'utente non esiste realmente ed è calcolato al volo. I server LDAP migliori lo memorizzano in entrambe le posizioni come ci si aspetterebbe – geoffc

1

Non riesco a darti un codice funzionante usando java naming ldap. ho usato primavera LDAP, e il modo in cui lo si fa: Prendi l'oggetto utente, fare una ricerca sul nome utente qualcosa come sAMAccountName = USERNAME

Dopo aver ottenuto l'oggetto che si retreive il memberOf proprietà -> questo sarà un elencare e controllare uno specifico in Java.

Questo è l'unico modo in cui potrei pensare.

2

Non sono sicuro delle specifiche dell'API Java, ma il modo generico di farlo è aggiungere un controllo di gruppo alla query/associazione.

3

metodi di ricerca LDAP di trovare se un utente è un membro di un gruppo non sono corrette, soprattutto se si sta parlando di un utente connesso. Per un utente che ha effettivamente effettuato l'accesso all'elenco di gruppi varia a seconda del computer su cui l'utente ha effettuato l'accesso. Tale elenco deve includere gruppi di trust di dominio, gruppi nidificati e gruppi locali.

Se stai cercando l'appartenenza al gruppo dell'utente attualmente connesso o di un utente che stai effettuando l'accesso con un nome utente e una password in Java, prova con Waffle.

IWindowsAuthProvider prov = new WindowsAuthProviderImpl(); 
IWindowsIdentity identity = prov.logonUser("username", "password"); 
System.out.println("User identity: " + identity.getFqn()); 
for(IWindowsAccount group : identity.getGroups()) { 
    System.out.println(" " + group.getFqn() + " (" + group.getSidString() + ")"); 
} 
+0

Questo mi sembra promettente. Ma ho trovato un problema qui. Ho creato un gruppo locale "Test". Sotto questo ho aggiunto manualmente l'utente corrente. E quando ho eseguito il codice sopra non mi ha mostrato il gruppo "Test". Qualsiasi puntatore mi aiuterebbe davvero su questo. –

+0

Assicurarsi che sia un gruppo di sicurezza, https://technet.microsoft.com/en-us/library/cc781446%28v=ws.10%29.aspx. –

12

Nessuno degli snippet di codice sopra indicati non ha funzionato per me. Dopo un giorno di spesa su Google e il codice sorgente di Tomcat, il codice ha funzionato bene per trovare gruppi di utenti.

import java.util.Hashtable; 

import javax.naming.CompositeName; 
import javax.naming.Context; 
import javax.naming.Name; 
import javax.naming.NameParser; 
import javax.naming.NamingEnumeration; 
import javax.naming.NamingException; 
import javax.naming.directory.Attribute; 
import javax.naming.directory.Attributes; 
import javax.naming.directory.InitialDirContext; 
import javax.naming.directory.SearchControls; 
import javax.naming.directory.SearchResult; 

public class MemberOfTest{ 
    private static final String contextFactory = "com.sun.jndi.ldap.LdapCtxFactory"; 
    private static final String connectionURL = "ldap://HOST:PORT"; 
    private static final String connectionName = "CN=Query,CN=Users,DC=XXX,DC=XX"; 
    private static final String connectionPassword = "XXX"; 

    // Optioanl 
    private static final String authentication = null; 
    private static final String protocol = null; 

    private static String username = "XXXX"; 

    private static final String MEMBER_OF = "memberOf"; 
    private static final String[] attrIdsToSearch = new String[] { MEMBER_OF }; 
    public static final String SEARCH_BY_SAM_ACCOUNT_NAME = "(sAMAccountName=%s)"; 
    public static final String SEARCH_GROUP_BY_GROUP_CN = "(&(objectCategory=group)(cn={0}))"; 
    private static String userBase = "DC=XXX,DC=XXX"; 

    public static void main(String[] args) throws NamingException { 
     Hashtable<String, String> env = new Hashtable<String, String>(); 

     // Configure our directory context environment. 

     env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory); 
     env.put(Context.PROVIDER_URL, connectionURL); 
     env.put(Context.SECURITY_PRINCIPAL, connectionName); 
     env.put(Context.SECURITY_CREDENTIALS, connectionPassword); 
     if (authentication != null) 
      env.put(Context.SECURITY_AUTHENTICATION, authentication); 
     if (protocol != null) 
      env.put(Context.SECURITY_PROTOCOL, protocol); 

     InitialDirContext context = new InitialDirContext(env); 
     String    filter = String.format(SEARCH_BY_SAM_ACCOUNT_NAME, username); 
     SearchControls  constraints = new SearchControls(); 
     constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); 
     constraints.setReturningAttributes(attrIdsToSearch); 
     NamingEnumeration results = context.search(userBase, filter,constraints); 
     // Fail if no entries found 
     if (results == null || !results.hasMore()) { 
      System.out.println("No result found"); 
      return; 
     } 

     // Get result for the first entry found 
     SearchResult result = (SearchResult) results.next(); 

     // Get the entry's distinguished name 
     NameParser parser = context.getNameParser(""); 
     Name contextName = parser.parse(context.getNameInNamespace()); 
     Name baseName = parser.parse(userBase); 

     Name entryName = parser.parse(new CompositeName(result.getName()) 
       .get(0)); 

     // Get the entry's attributes 
     Attributes attrs = result.getAttributes(); 
     Attribute attr = attrs.get(attrIdsToSearch[0]); 

     NamingEnumeration e = attr.getAll(); 
     System.out.println("Member of"); 
     while (e.hasMore()) { 
      String value = (String) e.next(); 
      System.out.println(value); 
     } 
    } 
} 
+1

La soluzione di Marcus contiene classi/librerie sconosciute (finché non le importi) e un altro codice irrilevante, mentre questa soluzione è semplice e funziona come un incantesimo. Grazie! – elady

5

Il modo più semplice è con 'ricerca': (per aprire un contesto Ldap: cerca sopra esempi)

/** 
    * Tests if an Active Directory user exists in an Active Directory group. 
    * @param ctx LDAP Context. 
    * @param dnADGroup distinguishedName of group. 
    * @param dnADUser distinguishedName of user. 
    * @return True if user is member of group. 
    */ 


public static boolean isMemberOfADGroup(LdapContext ctx, String dnADGroup, String dnADUser) { 
    try { 
    DirContext lookedContext = (DirContext) (ctx.lookup(dnADGroup)); 
    Attribute attrs = lookedContext.getAttributes("").get("member"); 
    for (int i = 0; i < attrs.size(); i++) { 
    String foundMember = (String) attrs.get(i); 
    if(foundMember.equals(dnADUser)) { 
    return true; 
    } 
    } 
    } catch (NamingException ex) { 
    String msg = "There has been an error trying to determin a group membership for AD user with distinguishedName: "+dnADUser; 
    System.out.println(msg); 
    ex.printStackTrace(); 
    } 
    return false; 
} 
0

ho trovato questo utile:

retrieves-group-membership for Active Directory

E io avere questo codice funzionante:

import java.util.Hashtable; 
import javax.naming.CompositeName; 
import javax.naming.Context; 
import javax.naming.Name; 
import javax.naming.NameParser; 
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.InitialDirContext; 
import javax.naming.directory.SearchControls; 
import javax.naming.directory.SearchResult; 
import javax.naming.ldap.InitialLdapContext; 
import javax.naming.ldap.LdapContext; 

public class TestAD1 { 

    private static String userBase = "DC=SomeName,DC=SomeName,DC=SomeName,DC=SomeName,DC=COM,DC=US"; 

    public static void main(String[] args) { 
     TestAD1 tad = new TestAD1(); 
     try { 
      // Create a LDAP Context 
      Hashtable env = new Hashtable(); 
      env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); 
      env.put(Context.SECURITY_AUTHENTICATION, "simple"); 
      env.put(Context.SECURITY_PRINCIPAL, "[email protected]"); 
      env.put(Context.SECURITY_CREDENTIALS, "drowssap"); 
      env.put(Context.PROVIDER_URL, "ldap://fully.qualified.server.name:389"); 
      LdapContext ctx = new InitialLdapContext(env, null); 
      InitialDirContext inidircontext = new InitialDirContext(env); 
      DirContext dirctx = new InitialLdapContext(env, null); 
      System.out.println("Connection Successful."); 

      // Print all attributes of the name in namespace 
      SearchControls sctls = new SearchControls(); 
      String retatts[] = {"sn", "mail", "displayName", "sAMAccountName"}; 
      sctls.setReturningAttributes(retatts); 
      sctls.setSearchScope(SearchControls.SUBTREE_SCOPE); 
      String srchfilter = "(&(objectClass=user)(mail=*))"; 
      String srchbase = userBase; 
      int totalresults = 0; 

      NamingEnumeration answer = dirctx.search(srchbase, srchfilter, sctls); 
      while (answer.hasMoreElements()) { 
       SearchResult sr = (SearchResult) answer.next(); 
       totalresults++; 
       System.out.println(">>> " + sr.getName()); 

       Attributes attrs = sr.getAttributes(); 
       if (answer == null || !answer.hasMore()) { 
        System.out.println("No result found"); 
        return; 
       } 

       if (attrs != null) { 
        try { 
         System.out.println(" surname: " + attrs.get("sn").get()); 
         System.out.println(" Email - ID: " + attrs.get("mail").get()); 
         System.out.println(" User - ID: " + attrs.get("displayName").get()); 
         System.out.println(" Account Name: " + attrs.get("sAMAccountName").get()); 
         tad.GetGroups(inidircontext, attrs.get("sAMAccountName").get().toString()); 
        } catch (NullPointerException e) { 
         System.out.println("Error listing attributes..." + e); 

        } 
       } 
       System.out.println("Total Results : " + totalresults); 
       // close dir context 
       dirctx.close(); 
      } 

      ctx.close(); 
     } catch (NamingException e) { 
      System.out.println("Problem Search Active Directory..." + e); 
      //e.printStackTrace(); 
     } 

    } 

    // Get all the groups. 

    public void GetGroups(InitialDirContext context, String username) throws NamingException { 
     String[] attrIdsToSearch = new String[]{"memberOf"}; 
     String SEARCH_BY_SAM_ACCOUNT_NAME = "(sAMAccountName=%s)"; 
     String filter = String.format(SEARCH_BY_SAM_ACCOUNT_NAME, username); 
     SearchControls constraints = new SearchControls(); 
     constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); 
     constraints.setReturningAttributes(attrIdsToSearch); 
     NamingEnumeration results = context.search(userBase, filter, constraints); 
     // Fail if no entries found 
     if (results == null || !results.hasMore()) { 
      System.out.println("No result found"); 
      return; 
     } 
     SearchResult result = (SearchResult) results.next(); 
     Attributes attrs = result.getAttributes(); 
     Attribute attr = attrs.get(attrIdsToSearch[0]); 

     NamingEnumeration e = attr.getAll(); 
     System.out.println(username + " is Member of the following groups : \n"); 
     while (e.hasMore()) { 
      String value = (String) e.next(); 
      System.out.println(value); 
     } 
    } 

} 
+0

Il codice di seguito riportato è sufficiente? – ajayfn

Problemi correlati