使用 Spring LDAP 进行用户认证
本节介绍使用 Spring LDAP 进行用户认证。它包含以下主题:
基本认证
虽然 ContextSource 的核心功能是为 LdapClient 和 LdapTemplate 提供 DirContext 实例,但您也可以使用它来针对 LDAP 服务器认证用户。ContextSource 的 getContext(principal, credentials) 方法正是实现了这一功能。它根据 ContextSource 配置构建一个 DirContext 实例,并使用提供的主体和凭据认证该上下文。一个自定义的认证方法可以像以下示例那样:
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,并且搜索总是从树的根(空路径)开始。一种更灵活的方法将允许您指定搜索基准、搜索过滤器和凭据。Spring LDAP 在 LdapClient 中包含一个 authenticate 方法,提供了此功能。
使用此方法时,认证变得像下面这样简单:
ldapClient.authenticate().query(query().where("uid").is("john.doe")).password("secret").execute();
| 如在已认证的上下文中执行操作中所述,某些设置可能要求您执行额外的操作以实现实际的认证。有关详细信息,请参阅在已认证的上下文中执行操作。 |
| 请勿编写自己的自定义认证方法。请使用 Spring LDAP 中提供的方法。 |
在已认证的上下文中执行操作
某些认证方案和 LDAP 服务器要求在创建的 DirContext 实例上执行一些操作,以使实际的认证发生。您应该测试并确保您的服务器设置和认证方案如何工作。否则可能导致用户无论提供的 DN 和凭据如何都被允许进入您的系统。以下示例展示了一个简单实现的认证方法,其中在已认证的上下文中执行了一个硬编码的 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 方法。
此方法允许在已认证的上下文中执行任何操作,如下所示:
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);
使用 Spring Security
虽然前面几节中描述的方法对于简单的认证场景可能已足够,但此领域的需求通常会迅速扩展。这涉及到认证、授权、Web 集成、用户上下文管理等多个方面。如果您怀疑需求可能会超出简单的认证范围,那么您绝对应该考虑使用 Spring Security 来实现您的安全目的。它是一个功能齐全、成熟的安全框架,可以解决上述以及其他几个方面的问题。