我有证据的概念工作应用程序,可以对Active Directory通过LDAP身份验证成功在测试服务器上,但生产中的应用将有超过TLS这样做 - 域控制器关闭不通过TLS发起的任何连接。
我已经安装在Eclipse中LDAP浏览器,和自己在其使用TLS我确实可以结合,但我不能为我的生活弄清楚如何让我的应用程序使用TLS。
ldap.xml:
<bean id="ldapAuthenticationProvider"
class="my.project.package.OverrideActiveDirectoryLdapAuthenticationProvider">
<!-- this works to authenticate by binding as the user in question -->
<constructor-arg value="test.server"/>
<constructor-arg value="ldap://192.168.0.2:389"/>
<!-- this doesn't work, because the server requires a TLS connection -->
<!-- <constructor-arg value="production.server"/> -->
<!-- <constructor-arg value="ldaps://192.168.0.3:389"/> -->
<property name="convertSubErrorCodesToExceptions" value="true"/>
</bean>
OverrideActiveDirectoryLdapAuthenticationProvider
是扩展Spring的副本的覆盖类ActiveDirectoryLdapAuthenticationProvider
类,它是指定某些原因final
。 我对覆盖的原因有自定义权限/当局填充用户对象的方式(我们将相关群体的任何使用组成员建立用户的权限,否则我们会从一个领域的AD用户对象上阅读)做。 在这里面,我只有重写loadUserAuthorities()
方法,但我怀疑我可能还需要重写bindAsUser()
方法或可能doAuthentication()
方法。
在XML和一个覆盖类是地方认证是由我的应用程序进行管理,而不是让春天做的工作只有两个地方。 我读过一些地方,为了使TLS我需要延长DefaultTlsDirContextAuthenticationStrategy
类,但我在哪里电线它吗? 是否有一个命名空间的解决方案? 我需要做什么东西完全(即放弃使用Spring的的ActiveDirectoryLdapAuthenticationProvider
,而使用LdapAuthenticationProvider
)?
任何帮助表示赞赏。
好了,大约一天的时间,并在其上工作半之后,我想通了。
我原来的办法是扩展Spring的ActiveDirectoryLdapAuthenticationProvider
类,并覆盖其loadUserAuthorities()
方法,以自定义验证用户的权限进行构建方式。 对于不明显的原因, ActiveDirectoryLdapAuthenticationProvider
类被指定为final
,所以我当然不能扩展。
幸运的是,开放源码提供了黑客(和该类的父类不 final
),所以我简单复制的它的全部内容,除去final
指定,并且相应地调整所述包和类的引用。 我没有在这个类编辑任何代码,除了增加一个非常明显的评论说,这不是对其进行编辑。 然后我扩展了这个类OverrideActiveDirectoryLdapAuthenticationProvider
,这也是我在我引用的ldap.xml
文件,并在其中加入了一个覆盖方法loadUserAuthorities
。 所有这一切都与一个简单的LDAP绑定通过未加密的会话伟大的工作(一个独立的虚拟服务器上)。
真正的网络环境要求所有的LDAP查询开始TLS握手,然而,被查询的服务器不是PDC - 它的名字是“sub.domain.tld`,但用户正确地验证对”使用domain.tld “。 此外,用户名必须用“NT_DOMAIN \”,以绑定前缀。 所有这一切都需要定制工作,不幸的是,我发现很少或根本没有帮助任何地方。
因此,这里是荒谬简单的改变,所有这些都在涉及进一步覆盖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
}
也就是说,我所做的这个方法是改变的方式bindPrincipal
字符串被格式化,我添加了一个键/值对哈希表。
我没有删除从子域domain
传递给我的类参数,因为这是一个由通过ldap.xml
; 我简单地改变那里的参数<constructor-arg value="domain.tld"/>
然后我改变了searchForUser()
在方法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});
最后一个变化是对createBindPrincipal()
方法,才能正确地构建字符串(对于我而言):
@Override
String createBindPrincipal(String username) {
if (domain == null || username.toLowerCase().endsWith(domain)) {
return username;
}
return "NT_DOMAIN\\" + username;
}
并与上述变化 - 这仍需要所有我的测试和headdesking的清理 - 我能够自己对Active Directory在网络本身,捕获我希望无论用户对象域绑定和身份验证,鉴定组成员等等。
哦,显然TLS不需要'LDAPS://',所以我ldap.xml
只是有ldap://192.168.0.3:389
。
TL;博士 :
要启用TLS,复制Spring的ActiveDirectoryLdapAuthenticationProvider
类,去掉final
名称,它在一个自定义类延伸,并覆盖bindAsUser()
加入env.put(Context.SECURITY_PROTOCOL, "tls");
环境哈希表。 而已。
为了更严密地控制绑定的用户名,域和LDAP查询字符串,重写应用相应的方法。 就我而言,我不能只是确定哪些值{0}
是的,所以我完全删除,并插入传递的username
字符串代替。
但愿有人在那里发现这是很有帮助的。