简体   繁体   English

Java LDAP - 确定用户是否在给定组中?

[英]Java LDAP - Determine if user in a given group?

We logon users to Active Directory via LDAP using the Java LDAP API.我们使用 Java LDAP API 通过 LDAP 将用户登录到 Active Directory。 We want to enhance our logon functionality to further check if the user is in a given AD group.我们希望增强我们的登录功能,以进一步检查用户是否在给定的 AD 组中。 Does anyone know how to do this?有谁知道如何做到这一点?

Current code:当前代码:

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

Update : See the solution below.更新:请参阅下面的解决方案。

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

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.在 Google 和 tomcat 源代码上花费 1 天后,以下代码可以很好地找到用户组。

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

The easiest way is with 'lookup': (to open an Ldap Context: look above examples)最简单的方法是使用“查找”:(打开一个 Ldap 上下文:查看上面的示例)

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

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.用于查找用户是否为组成员的 LDAP 查找方法不正确,尤其是在您谈论已登录用户时。 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 .如果您正在寻找当前登录用户的组成员身份或您使用 Java 中的用户名和密码登录的用户,请尝试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() + ")"); 
} 

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:跟进 Sundaramurthi 的回答,它可以以更直接的方式完成,您无需查询所有用户组:

(&(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其中 XXXX - 用户名 XXX.XX - 域名 YYY - 组名

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

不确定 Java API 细节,但这样做的通用方法是向查询/绑定添加组检查。

I can't give you a working code using java naming ldap.我不能给你一个使用 java 命名 ldap 的工作代码。 I used Spring LDAP, and the way you do it: Get the User object, do a search on the username something like sAMAccountName=USERNAME我使用了 Spring LDAP,以及你这样做的方式:获取用户对象,对用户名进行搜索,例如 sAMAccountName=USERNAME

After you get the object you retreive the property memberOf -> this will be a list and check for a specific one in Java.获得对象后,您检索属性 memberOf -> 这将是一个列表,并在 Java 中检查特定的列表。

That is the only way I could think of.这是我能想到的唯一方法。

To query if a user belongs to a given group once you have established a connection you can query using either member or memberOf attribute in Active Directory.要在建立连接后查询用户是否属于给定组,您可以使用 Active Directory 中的 member 或 memberOf 属性进行查询。

Using memberOf Attribute : filter used : (&(Group Member Attribute=Group DN)(objectClass=Group Object class)) Ex : (&(memberOf=CN=group,ou=qa_ou,dc=ppma,dc=org)(objectClass=group))使用 memberOf 属性:使用的过滤器 : (&(Group Member Attribute=Group DN)(objectClass=Group Object class)) 例如:(&(memberOf=CN=group,ou=qa_ou,dc=ppma,dc=org)(objectClass =组))

Using member Attribute : filter used : (&(Group Member Attribute=User DN)(objectClass=Group Object class)) Ex : (&(member=CN=user,ou=qa_ou,dc=ppma,dc=org)(objectClass=group)) Using member Attribute : filter used : (&(Group Member Attribute=User DN)(objectClass=Group Object class)) 例如:(&(member=CN=user,ou=qa_ou,dc=ppma,dc=org)(objectClass =组))

I found this useful:我发现这很有用:

retrieves-group-membership for Active Directory 检索 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);
        }
    }

}

Also you can modify the accepted answer from here: Authenticating against Active Directory with Java on Linux with the following:您也可以从此处修改已接受的答案: 在 Linux使用 Java 对 Active Directory 进行身份验证,如下所示:

String group="name of the group";
Iterator ig = groups.iterator();
Boolean bool=false;
while (ig.hasNext()) {
      String a=ig.next().toString();
      if (a.equals(group)) { 
                JOptionPane.showMessageDialog(this, "Authentication succeeded!");   
                bool=true;
                // here you can do smth in case of success  
      }
}
if (bool==false){
             JOptionPane.showMessageDialog(this, "Permission denied");   
}

Unfortunately the answer varies with installations of AD as well as other types of LDAP server.不幸的是,答案因 AD 以及其他类型的 LDAP 服务器的安装而异。

We had to solve the same problem.我们必须解决同样的问题。 In the end we allowed the system administrator to provide us with an LDAP query-pattern where we substitute the user name (and group name if that needs to be variable too) into the pattern.最后,我们允许系统管理员为我们提供一个 LDAP 查询模式,我们将用户名(如果也需要可变,则组名)替换到模式中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM