好的,经过大约一天半的工作,我终于解决了问题。
我的原始方法是扩展Spring的
ActiveDirectoryLdapAuthenticationProvider
类,并覆盖其
loadUserAuthorities()
方法,以便自定义构建认证用户权限的方式。由于一些不明显的原因,
ActiveDirectoryLdapAuthenticationProvider
类被指定为
final
,所以我无法对其进行扩展。
值得庆幸的是,开源提供了黑客的方法(该类的超类并非
final
),所以我只需复制整个内容,删除
final
指示,并相应调整包和类引用。我没有编辑此类中的任何代码,除了添加一个高度可见的注释,其中写着不要编辑它。然后,在
OverrideActiveDirectoryLdapAuthenticationProvider
中扩展了这个类,我还在其中添加了一个
loadUserAuthorities
的重载方法,并在我的
ldap.xml
文件中引用了它。所有这些都可以通过简单的LDAP绑定在未加密会话上(在隔离的虚拟服务器上)很好地工作。
真正的网络环境要求所有LDAP查询都必须从TLS握手开始,而被查询的服务器不是PDC——它的名称是'sub.domain.tld',但用户已正确验证为'domain.tld'。此外,用户名必须以'NT_DOMAIN\'开头才能进行绑定。所有这些都需要自定义工作,而不幸的是,在任何地方我都找不到太多帮助。
所以这里是可笑简单的更改,所有这些都涉及在
OverrideActiveDirectoryLdapAuthenticationProvider
中进一步重载:
@Override
protected DirContext bindAsUser(String username, String password) {
final String bindUrl = url;
Hashtable<String,String> env = new Hashtable<String,String>();
env.put(Context.SECURITY_AUTHENTICATION, "simple");
String bindPrincipal = "NT_DOMAIN\\" + username;
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");
env.put(Context.SECURITY_PROTOCOL, "tls");
}
也就是说,我所做的只是更改了bindPrincipal
字符串的格式,并向哈希表中添加了一个键/值对。
我不需要从传递给我的类的domain
参数中删除子域名,因为这是通过ldap.xml
传递的;我只是在那里将参数更改为<constructor-arg value="domain.tld"/>
然后我更改了OverrideActiveDirectoryLdapAuthenticationProvider
中的searchForUser()
方法:
@Override
protected DirContextOperations searchForUser(DirContext ctx, String username) throws NamingException {
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
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});
最后一次更改是对
createBindPrincipal()
方法进行修改,以便正确地构建字符串(根据我的需求):
@Override
String createBindPrincipal(String username) {
if (domain == null || username.toLowerCase().endsWith(domain)) {
return username;
}
return "NT_DOMAIN\\" + username;
}
通过上述更改 -- 我仍然需要对所有测试和调试的内容进行清理 -- 我能够绑定并认证自己对网络中的Active Directory进行操作,捕获我想要的用户对象字段,识别组成员等等。
哦,还有明显TLS并不需要 'ldaps://',所以我的ldap.xml
只需使用 ldap://192.168.0.3:389
。
tl;dr:
为启用TLS,请复制Spring的ActiveDirectoryLdapAuthenticationProvider
类,删除final
指示,将其扩展到自定义类中,并重写bindAsUser()
, 同时将env.put(Context.SECURITY_PROTOCOL, "tls");
添加到环境哈希表中即可。
如果要更精确地控制绑定用户名、域和LDAP查询字符串,请根据需要重写适当的方法。在我的情况下,我无法确定{0}
的值是什么,所以我完全移除了它并插入了传递的username
字符串。
希望这对某些人有所帮助。