2009-02-20 14 views
23

Iniciamos sesión en Active Directory a través de LDAP utilizando la API Java LDAP. Queremos mejorar nuestra funcionalidad de inicio de sesión para verificar si el usuario está en un grupo de AD determinado. ¿Alguien sabe como hacer esto?Java LDAP: ¿Determina si el usuario está en un grupo determinado?

código actual:

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 { 
    ... 
} 

actualización: Ver la solución a continuación.

Respuesta

18

Hemos resuelto esto con la clase a continuación. Simplemente llama al método autenticar:

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

Tenga en cuenta que esto no es del todo completo porque los grupos pueden anidarse en Active Directory, por lo que no obtendrá usuarios que sean miembros de un grupo por ser miembro de otro grupo. La forma correcta de hacerlo es obtener el atributo tokenGroups del usuario que contiene los grupos como una lista de SID, sin embargo, este es un atributo binario que debe decodificarse. Ver: http://blogs.msdn.com/alextch/archive/2007/06/18/sample-java-application-that-retrieves-group-membership-of-an-active-directory-user-account.aspx –

1

Lamentablemente, la respuesta varía según las instalaciones de AD y de otros tipos de servidor LDAP.

Tuvimos que resolver el mismo problema. Al final permitimos que el administrador del sistema nos proporcione un patrón de consulta LDAP donde sustituimos el patrón con el nombre de usuario (y el nombre del grupo, si eso también debe ser variable).

+0

Gracias. ¿Por qué la respuesta varía? Parece ser una consulta bastante estándar. ¿Tiene una muestra de código que utiliza su consulta LDAP? –

+0

Sospecho que varía en qué versión de AD debido a cómo AD maneja memberOf. Almacena membresía de grupo en el grupo y no en el objeto de usuario. El atributo memberOf en el usuario realmente no existe y se calcula sobre la marcha. Los mejores servidores LDAP lo almacenan en ambos lugares como cabría esperar – geoffc

1

No puedo darle un código de trabajo usando java name ldap. que utiliza la primavera de LDAP, y la forma en que lo hace: Obtener el objeto de usuario, realice una búsqueda en el nombre de usuario algo así como sAMAccountName = Nombre de usuario

Después de obtener el objeto que retreive la memberOf propiedad -> Esta será una liste y verifique uno específico en Java.

Esa es la única forma en que se me ocurre.

2

No estoy seguro acerca de las especificaciones de la API de Java, pero la forma genérica de hacerlo es agregar una verificación de grupo a la consulta/enlace.

3

métodos de búsqueda LDAP de encontrar si un usuario es miembro de un grupo no son correctas, sobre todo si estamos hablando de un usuario conectado. Para un usuario que está realmente conectado, la lista de grupos varía según la computadora en la que el usuario inició sesión. Esa lista debe incluir grupos de trusts de dominio, grupos anidados y grupos locales.

Si está buscando membresías grupales del usuario que está actualmente conectado o un usuario que está iniciando sesión con un nombre de usuario y contraseña en Java, intente 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

Esto me parece prometedor. Pero encontré un problema aquí. Creé un grupo local "Test". Debajo de esto agregué manualmente el usuario actual. Y cuando corrí por encima del código, no me mostró el grupo "Prueba". Cualquier apuntador realmente me ayudaría en esto. –

+0

Asegúrese de que sea un grupo de seguridad, https://technet.microsoft.com/en-us/library/cc781446%28v=ws.10%29.aspx. –

12

Ninguno de los fragmentos de código anteriores no funcionó para mí. Después de 1 día, gastar en Google y Tomcat source following code funcionó bien para encontrar grupos de usuarios.

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 solución de Marcus contiene clases/bibliotecas desconocidas (hasta que las importa) y otro código irrelevante, mientras que esta solución es simple y funciona como un amuleto. ¡Gracias! – elady

5

La forma más fácil es con 'lookup': (a abrir un Contexto LDAP: mirar por encima de ejemplos)

/** 
    * 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

He encontrado esto útil:

retrieves-group-membership for Active Directory

Y yo tiene esta pieza de código de trabajo:

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

¿Es suficiente el siguiente código? – ajayfn

Cuestiones relacionadas