我们使用Java LDAP API通过LDAP将用户登录到Active Directory.我们希望增强登录功能,以进一步检查用户是否在给定的AD组中.有谁知道如何做到这一点?
当前代码:
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 { ... }
更新:请参阅下面的解决方案.
我们用下面的课程解决了这个问题.只需调用authenticate方法:
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; } }
上面的代码片段都没有对我有用.在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 { Hashtableenv = new Hashtable (); // 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); } } }
最简单的方法是使用'lookup':(打开Ldap Context:看上面的例子)
/** * 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; }