当前位置: 首页 > 知识库问答 >
问题:

如何通过LDAP over TLS对Active Directory进行身份验证?

薛钧
2023-03-14

我有一个工作的概念验证应用程序,它可以通过测试服务器上的LDAP成功地对Active Directory进行身份验证,但是生产应用程序必须通过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 user对象上的字段读取)。在其中,我只重写loadUserAuthorities()方法,但我怀疑我可能还需要重写bindasuser()方法或doAuthentication()方法。

XML和一个override类是我的应用程序管理身份验证的唯一两个地方,而不是让Spring来完成这项工作。我读过几个地方,为了启用TLS,我需要扩展DefaultTLSDirContextAuthEnticationStrategy类,但是我应该把它连接到哪里呢?有名称空间解决方案吗?我是否需要完全执行其他操作(即放弃使用Spring的ActiveDirectoryLDAPAuthenticationProvider,而是使用LDAPAuthenticationProvider)?

共有1个答案

谭琛
2023-03-14

好吧,经过一天半的努力,我终于想通了。

我最初的方法是扩展Spring的ActiveDirectoryLDAPAuthenticationProvider类,并重写其LoadUserAuthorities()方法,以便自定义构建经过身份验证的用户权限的方式。由于不明显的原因,ActiveDirectoryLDAPAuthenticationProvider类被指定为Final,因此我当然不能扩展它。

谢天谢地,开放源代码提供了黑客攻击(而且该类的超类不是final),所以我简单地复制了它的全部内容,删除了final指定,并相应地调整了包和类引用。我没有编辑这个类中的任何代码,除了添加一个高度可见的注释,说明不要编辑它。然后,我在OverrideActiveDirectoryLDAPAuthenticationProvider中扩展了该类,我也在ldap.xml文件中引用了该类,并在其中添加了LoadUserAuthoritions的重写方法。通过在未加密的会话上(在隔离的虚拟服务器上)进行简单的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字符串的格式,并向哈希表添加了一个键/值。

然后,我更改了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;
}

通过上述更改--这些更改仍然需要从所有的测试和头盘工作中清除--我能够以自己的身份在网络上与Active Directory进行绑定和身份验证,捕获我希望的任何用户对象字段,标识组成员身份,等等。

哦,而且显然TLS不需要'ldaps://',所以我的ldap.xml只需要ldap://192.168.0.3:389

TL;DR:

若要启用TLS,请复制Spring的ActiveDirectoryLDAPAuthenticationProvider类,删除final指定,在自定义类中扩展它,并通过向环境Hashtable添加env.put(context.security_protocol,“TLS”);重写bindasuser()。就是这样。

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

希望有人发现这有帮助。

 类似资料:
  • 目前用户名总是匿名的。设置调用方主体的正确方法是什么?我在拦截器里做这个吗?还是在豆子本身?我的目标是基本上能够调用一个方法loginUserWithEJBOnJboss(String user,String pass),该方法使用在jboss中配置的登录方法并正确设置主体。 我在这里不知所措,谷歌什么也没找到。也许我只是在寻找错误的单词。

  • 在我配置了下面的配置之后,它不会连接到Active Directory。我无法使用Active Directory的帐户登录。会有什么问题? 我有一个Ubuntu服务器18.04,带有ApacheGuacamoleV1。0.0. 安装。我想使用LDAP身份验证来验证用户。我已经下载了鳄梨酱-auth-ldap-1.0.0。jar和jldap-4.3。jar扩展。 10.10.10.21,10.10

  • 问题: 我们有一个spring的基于MVC的RESTful API,它包含敏感信息。API应该是安全的,但是不希望在每个请求中发送用户的凭据(User/Pass组合)。根据REST指南(和内部业务需求),服务器必须保持无状态。API将由另一台服务器以混搭方式使用。 要求: > 客户端请求使用凭据(不受保护的URL);服务器返回一个安全令牌,该令牌包含足够的信息,供服务器验证未来的请求并保持无状态。

  • 问题内容: 我有一个有效的概念验证应用程序,可以通过测试服务器上的LDAP通过Active Directory进行成功的身份验证,但是生产应用程序必须通过TLS进行身份验证-域控制器会关闭所有未通过TLS发起的连接。 我已经在Eclipse中安装了LDAP浏览器,并且确实可以在 其中 使用TLS进行绑定,但是我一生都无法弄清楚如何让我的应用程序使用TLS。 ldap.xml : 是重写类,它扩展了

  • 我是Spring安全的新手,我想用数据库验证用户。我已经用jdbc创建了一个登录页面和一个身份验证提供程序,它检查用户是否存在于数据库中。但是我的代码没有这样做的问题是,它允许所有用户登录!我的代码怎么了?谢谢你的帮助。 这是我的安全会议。xml:

  • 我有一个react应用程序在一个单独的端口(localhost:3000)上运行,我想用它来验证用户,目前我的Spring后端(localhost:8080)有一个代理设置。 我能以某种方式手动验证而不是通过发送一个请求到我的后端,并获得一个会话cookie,然后在每个请求中包含cookie吗?这也将简化iOS方面的验证过程(使用此过程,我只能将会话cookie值存储在keychain中,并在每次