如何对Active Directory通过LDAP通过TLS认证?(How to authentic

2019-09-03 10:02发布

我有证据的概念工作应用程序,可以对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 )?

任何帮助表示赞赏。

Answer 1:

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

我原来的办法是扩展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字符串代替。

但愿有人在那里发现这是很有帮助的。



文章来源: How to authenticate against Active Directory via LDAP over TLS?