User Authentication using Spring LDAP

本部分涵盖了使用 Spring LDAP 进行用户身份验证的内容。其中包含以下主题:

Basic Authentication

虽然 ContextSource 的核心功能是为 LdapClientLdapTemplate 提供 DirContext 实例,但你也可以使用它对 LDAP 服务器的身份验证用户。ContextSourcegetContext(principal, credentials) 方法的作用与此相同。它根据 ContextSource 配置构建一个 DirContext 实例,并使用指定的 principal 和 credentials 对该上下文进行身份验证。自定义身份验证方法可以像以下示例所示:

public boolean authenticate(String userDn, String credentials) {
  DirContext ctx = null;
  try {
    ctx = contextSource.getContext(userDn, credentials);
    return true;
  } catch (Exception e) {
    // Context creation failed - authentication did not succeed
    logger.error("Login failed", e);
    return false;
  } finally {
    // It is imperative that the created DirContext instance is always closed
    LdapUtils.closeContext(ctx);
  }
}

提供给 authenticate 方法的 userDn 需要是要进行身份验证的用户的完整 DN(而不管 ContextSource 上的 base 设置如何)。你通常需要根据(例如)用户名执行 LDAP 搜索来获取此 DN。以下示例展示了如何进行此操作:

private String getDnForUser(String uid) {
  List<String> result = ldapClient.search()
      .query(query().where("uid").is(uid))
      .toList((Object ctx) -> ((DirContextOperations) ctx).getNameInNamespace());

  if(result.size() != 1) {
    throw new RuntimeException("User not found or not unique");
  }

  return result.get(0);
}

这种方法有一些缺点。你被迫关心用户的 DN,你只能搜索用户的 uid,而且搜索始终从树的根(空路径)开始。更灵活的方法会让你指定搜索基准、搜索过滤器和 credentials。Spring LDAP 在 LdapClient 中包含了一个提供此功能的身份验证方法。

当你使用此方法时,身份验证将变得像以下一样简单:

Example 1. Authenticating a user using Spring LDAP
ldapClient.authenticate().query(query().where("uid").is("john.doe")).password("secret").execute();

Performing Operations on the Authenticated Context 中所述,在某些设置中,可能需要执行额外操作才能进行实际身份验证。有关详细信息,请参阅 Performing Operations on the Authenticated Context

不要编写您自己的自定义 authenticate 方法。使用 Spring LDAP 中提供的那些方法。

Performing Operations on the Authenticated Context

某些身份验证方案和 LDAP 服务器要求在创建的 DirContext 实例上执行某些操作才能进行实际身份验证。你应该测试并确保服务器设置和身份验证方案的行为方式。如果不这么做,可能会导致用户无论指定的 DN 和 credentials 如何而被接纳到你的系统中。以下示例展示了一个幼稚的身份验证方法实现,其中对经过身份验证的上下文执行硬编码的 lookup 操作:

public boolean myAuthenticate(String userDn, String credentials) {
  DirContext ctx = null;
  try {
    ctx = contextSource.getContext(userDn, credentials);
    // Take care here - if a base was specified on the ContextSource
    // that needs to be removed from the user DN for the lookup to succeed.
    *ctx.lookup(userDn);*
    return true;
  } catch (Exception e) {
    // Context creation failed - authentication did not succeed
    logger.error("Login failed", e);
    return false;
  } finally {
    // It is imperative that the created DirContext instance is always closed
    LdapUtils.closeContext(ctx);
  }
}

如果将操作作为回调接口的实现提供,而不是限制操作始终为 lookup,那就更好。Spring LDAP 包含 AuthenticatedLdapEntryContextMapper 回调接口和相应的 authenticate 方法。

此方法允许对经过身份验证的上下文执行任何操作,如下所示:

Example 2. Performing an LDAP operation on the authenticated context using Spring LDAP
AuthenticatedLdapEntryContextMapper<DirContextOperations> mapper = new AuthenticatedLdapEntryContextMapper<DirContextOperations>() {
  public DirContextOperations mapWithContext(DirContext ctx, LdapEntryIdentification ldapEntryIdentification) {
    try {
      return (DirContextOperations) ctx.lookup(ldapEntryIdentification.getRelativeName());
    }
    catch (NamingException e) {
      throw new RuntimeException("Failed to lookup " + ldapEntryIdentification.getRelativeName(), e);
    }
  }
};

ldapClient.authenticate().query(query().where("uid").is("john.doe")).password("secret").execute(mapper);

Obsolete Authentication Methods

除了前面部分中描述的 authenticate 方法之外,你还可以使用许多弃用的身份验证方法。虽然这些方法工作正常,但我们建议改用 LdapQuery 方法。

Using Spring Security

尽管上一部分中描述的方法可能足以用于简单的身份验证场景,但此领域的要求通常会迅速增加。应用的方面很多,包括身份验证、授权、Web 集成、用户上下文管理等。如果您怀疑要求可能扩展到不只是简单身份验证,那么您肯定应该考虑将 @"33" 用于您的安全目的。它是一个成熟的全功能安全框架,它可以解决上述方面以及其他一些方面。