4

我有证据的概念工作应用程序,可以对Active Directory通过LDAP身份验证成功在测试服务器上,但生产中的应用将有超过TLS这样做 - 域控制器关闭不通过发起任何连接TLS。如何通过TLS通过LDAP对Active Directory进行身份验证?

我已经安装在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)?

任何帮助表示赞赏。

回答

6

好了,大约一天的时间,并在其上工作半之后,我想通了。

我最初的做法是扩展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"/>

然后我在OverrideActiveDirectoryLdapAuthenticationProvider改变了searchForUser()方法:

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

随着上述变化 - 这仍然需要从我所有的测试和头饰清理 - 我能够绑定d在网络上对Active Directory进行身份验证,捕获我希望的任何用户对象字段,确定组成员资格等。

哦,显然TLS不需要'ldaps://',因此我的ldap.xml只是简单的有ldap://192.168.0.3:389


TL;博士

要启用TLS,复制Spring的ActiveDirectoryLdapAuthenticationProvider类,去掉final名称,在自定义类扩展,并通过添加env.put(Context.SECURITY_PROTOCOL, "tls");环境哈希表重写bindAsUser()。而已。

要更好地控制绑定用户名,域和LDAP查询字符串,请根据需要重写适用的方法。在我的情况下,我无法确定{0}的值是什么,所以我完全删除它,并插入传递的username字符串。

希望有人在那里发现这有帮助。

相关问题