Java LDAP - Determine if user in a given group?

后端 未结 10 784
时光取名叫无心
时光取名叫无心 2020-12-12 20:18

We logon users to Active Directory via LDAP using the Java LDAP API. We want to enhance our logon functionality to further check if the user is in a given AD group. Does a

相关标签:
10条回答
  • 2020-12-12 20:48

    LDAP lookup methods of finding whether a user is a member of a group are not correct, especially if you're talking about a logged on user. For a user that's actually logged on the list of groups varies depending on which computer the user logged on. That list needs to include groups from domain trusts, nested groups and local groups.

    If you're looking for group memberships of the currently logged on user or a user that you're logging on with a username and password in Java, try 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 讨论(0)
  • 2020-12-12 20:49

    Following up on Sundaramurthi's answer, it could be done even more straightforward way, where you don't query for all the user's group:

    (&(objectClass=user)(sAMAccountName=XXXX)(memberOf=CN=YYY,OU=_Common-Access,OU=Groups,OU=_CORP,DC=XXX,DC=XX))
    

    where XXXX - user name XXX.XX - domain name YYY - group name

    This lets you to just get an answer whether user is in a group or not.

    So just do:

    String userBase = "DC=XXX,DC=XX";
    String CHECK_IF_USER_IN_GROUP = "(&(objectClass=user)(sAMAccountName=%s)(memberOf=CN=%s,OU=...,OU=...,OU=...,%s))";
    
    String queryFilter = String.format(CHECK_IF_USER_IN_GROUP, user, group, userBase);
    
    SearchControls constraints = new SearchControls();
    constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
    
    NamingEnumeration results = context.search(userBase, queryFilter, constraints);
    
    if (results == null) {
        throw new Exception("No answer from LDAP");
    }
    
    if (!results.hasMore()) {
        System.out.println("No result found");
        // user is not in the group
    }
    // user is in the group
    
    0 讨论(0)
  • 2020-12-12 20:50

    We solved this with the class below. Just call the authenticate method:

    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;
        }
    }
    
    0 讨论(0)
  • 2020-12-12 20:54

    I found this useful:

    retrieves-group-membership for Active Directory

    And I have this piece of working code:

    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, "ldap.serviceaccount@domain.com");
                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 讨论(0)
  • 2020-12-12 20:55

    None of above code snippets didn't worked for me. After 1 day spending on Google and tomcat source following code worked well to find user groups.

    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);
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-12 20:56

    Not sure about Java API specifics, but the generic way of doing this is adding a group check to the query/binding.

    0 讨论(0)
提交回复
热议问题