Okay, so after about a day and a half of working on it, I realized.
My initial approach was to extend the Spring class ActiveDirectoryLdapAuthenticationProvider and override its loadUserAuthorities() method to set up a way to create authenticated user rights. For non-obvious reasons, the ActiveDirectoryLdapAuthenticationProvider class ActiveDirectoryLdapAuthenticationProvider designated as final , so of course I cannot extend it.
Fortunately, open source provides hacking (and the superclass of the class is not final ), so I just copied all its contents, removed the final notation and adjusted the links to packages and classes accordingly. I have not edited the code in this class, except to add a very noticeable comment saying that it does not edit it. Then I extended this class to OverrideActiveDirectoryLdapAuthenticationProvider , which was also mentioned in my ldap.xml file, and it added an override method for loadUserAuthorities . All of this worked great with a simple LDAP binding to an unencrypted session (on an isolated virtual server).
The real network environment requires that all LDAP requests start with TLS handshaking, however the requested server is not a PDC - its name is "sub.domain.tld", but the user is correctly authenticated against 'domain.tld. In addition, the username must be added with the name "NT_DOMAIN \" to bind. All this required configuration work, and, unfortunately, I did not find or did not find anything.
So, here are the ridiculously simple changes, all of which are related to additional overrides in OverrideActiveDirectoryLdapAuthenticationProvider :
@Override protected DirContext bindAsUser(String username, String password) { final String bindUrl = url; //super reference Hashtable<String,String> env = new Hashtable<String,String>(); env.put(Context.SECURITY_AUTHENTICATION, "simple"); //String bindPrincipal = createBindPrincipal(username); String bindPrincipal = "NT_DOMAIN\\" + username; //the bindPrincipal() method builds the principal name incorrectly env.put(Context.SECURITY_PRINCIPAL, bindPrincipal); env.put(Context.PROVIDER_URL, bindUrl); env.put(Context.SECURITY_CREDENTIALS, password); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxtFactory"); //and finally, this simple addition env.put(Context.SECURITY_PROTOCOL, "tls"); //. . . try/catch portion left alone }
That is, everything I did with this method changed the way the bindPrincipal string was bindPrincipal , and I added the key / value to the hash table.
I did not need to remove the subdomain from the domain parameter passed to my class, because it was passed ldap.xml ; I just changed the parameter there to <constructor-arg value="domain.tld"/>
Then I changed the searchForUser() method to OverrideActiveDirectoryLdapAuthenticationProvider :
@Override protected DirContextOperations searchForUser(DirContext ctx, String username) throws NamingException { SearchControls searchCtls = new SearchControls(); searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); //this doesn't work, and I'm not sure exactly what the value of the parameter {0} is //String searchFilter = "(&(objectClass=user)(userPrincipalName={0}))"; String searchFilter = "(&(objectClass=user)(userPrincipalName=" + username + "@domain.tld))"; final String bindPrincipal = createBindPrincipal(username); String searchRoot = rootDn != null ? rootDn : searchRootFromPrincipal(bindPrincipal); return SpringSecurityLdapTemplate.searchForSingleEntryInternal(ctx, searchCtls, searchRoot, searchFilter, new Object[]{bindPrincipal});
The last change was in the createBindPrincipal() method to build the string correctly (for my purposes):
@Override String createBindPrincipal(String username) { if (domain == null || username.toLowerCase().endsWith(domain)) { return username; } return "NT_DOMAIN\\" + username; }
And with the above changes, which still need to be cleared of all my tests and bells and whistles - I was able to connect and authenticate as I am against Active Directory on the network, capture all the fields of user objects that I wanted, define group membership, etc.
Oh, and apparently TLS does not require "ldaps: //", so my ldap.xml just has ldap://192.168.0.3:389 .
TL; DR :
To enable TLS, copy the Spring class ActiveDirectoryLdapAuthenticationProvider , remove the final notation, extend it in the user class and override bindAsUser() by adding env.put(Context.SECURITY_PROTOCOL, "tls"); to the hash table of the environment. What is it.
To better control the binding username, domain, and LDAP query sequence, use the appropriate methods. In my case, I could not determine what the value {0} , so I completely deleted it and inserted the passed username string instead.
Hope someone out there finds this helpful.