跳到主要内容
版本:7.0.2

LDAP 认证

DeepSeek V3 中英对照 LDAP LDAP Authentication

LDAP(轻量级目录访问协议)常被组织用作存储用户信息的中央仓库和认证服务。它也可用于存储应用程序用户的角色信息。

Spring Security 的基于 LDAP 的身份验证在配置为接受用户名/密码进行身份验证时使用。然而,尽管使用用户名和密码进行身份验证,它并不使用 UserDetailsService,因为在绑定身份验证中,LDAP 服务器不返回密码,因此应用程序无法执行密码验证。

LDAP服务器的配置方式多种多样,因此Spring Security的LDAP提供程序具备完全可配置性。它通过独立的策略接口处理身份验证和角色检索,并提供了默认实现,这些实现可通过配置来适应各种不同的应用场景。

必需依赖项

要开始使用,请将 spring-security-ldap 依赖项添加到您的项目中。使用 Spring Boot 时,请添加以下依赖项:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-ldap</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>

先决条件

在尝试将 LDAP 与 Spring Security 结合使用之前,您应当熟悉 LDAP。以下链接提供了相关概念的详细介绍,并指导如何使用免费的 LDAP 服务器 OpenLDAP 设置目录:www.zytrax.com/books/ldap/。了解用于从 Java 访问 LDAP 的 JNDI API 也会有所帮助。在 LDAP 提供程序中,我们没有使用任何第三方 LDAP 库(如 Mozilla、JLDAP 或其他),但大量使用了 Spring LDAP,因此如果您计划添加自定义配置,熟悉该项目可能会很有帮助。

在使用LDAP认证时,应确保正确配置LDAP连接池。若不熟悉配置方法,请参阅Java LDAP文档

设置嵌入式 LDAP 服务器

首先,你需要确保有一个LDAP服务器来指向你的配置。为简化起见,通常最好从嵌入式LDAP服务器开始。Spring Security支持使用以下两种方式:

在以下示例中,我们将 users.ldif 作为类路径资源公开,用于初始化嵌入式 LDAP 服务器,其中包含两个用户:useradmin,两者的密码均为 password

dn: ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: groups

dn: ou=people,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: people

dn: uid=admin,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Rod Johnson
sn: Johnson
uid: admin
userPassword: password

dn: uid=user,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Dianne Emu
sn: Emu
uid: user
userPassword: password

dn: cn=user,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: user
member: uid=admin,ou=people,dc=springframework,dc=org
member: uid=user,ou=people,dc=springframework,dc=org

dn: cn=admin,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: admin
member: uid=admin,ou=people,dc=springframework,dc=org

嵌入式 UnboundID 服务器

若希望使用 UnboundID,请指定以下依赖项:

<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
<version>7.0.4</version>
<scope>runtime</scope>
</dependency>

随后,您可以使用 EmbeddedLdapServerContextSourceFactoryBean 来配置嵌入式 LDAP 服务器。这将指示 Spring Security 启动一个内存中的 LDAP 服务器:

@Bean
public EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
return EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer();
}

或者,您也可以手动配置嵌入式 LDAP 服务器。如果选择这种方式,您将需要负责管理嵌入式 LDAP 服务器的生命周期。

@Bean
UnboundIdContainer ldapContainer() {
return new UnboundIdContainer("dc=springframework,dc=org",
"classpath:users.ldif");
}

嵌入式 ApacheDS 服务器

Spring Security 7 移除了对 Apache DS 的支持。请改用 UnboundID

LDAP ContextSource

一旦你有了一个可供配置指向的LDAP服务器,就需要配置Spring Security以指向用于用户身份验证的LDAP服务器。为此,需要创建一个LDAP ContextSource(这相当于JDBC的 DataSource)。如果你已经配置了 EmbeddedLdapServerContextSourceFactoryBean,Spring Security 将创建一个指向嵌入式LDAP服务器的LDAP ContextSource

@Bean
public EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean =
EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer();
contextSourceFactoryBean.setPort(0);
return contextSourceFactoryBean;
}

或者,你也可以显式配置 LDAP ContextSource 以连接到所提供的 LDAP 服务器:

ContextSource contextSource(UnboundIdContainer container) {
return new DefaultSpringSecurityContextSource("ldap://localhost:53389/dc=springframework,dc=org");
}

身份验证

Spring Security 的 LDAP 支持不使用 UserDetailsService,因为 LDAP 绑定认证不允许客户端读取密码,甚至无法读取密码的哈希版本。这意味着 Spring Security 无法读取密码并进行认证。

因此,LDAP支持是通过LdapAuthenticator接口实现的。LdapAuthenticator接口还负责检索任何所需的用户属性。这是因为属性的权限可能取决于所使用的身份验证类型。例如,如果以用户身份绑定,可能需要使用用户自身的权限来读取属性。

Spring Security 提供了两种 LdapAuthenticator 实现:

使用绑定认证

绑定认证 是使用 LDAP 进行用户身份验证的最常见机制。在绑定认证中,用户的凭据(用户名和密码)被提交给 LDAP 服务器,由服务器进行验证。使用绑定认证的优势在于,用户的秘密(密码)无需暴露给客户端,这有助于防止其泄露。

以下示例展示了绑定认证配置:

@Bean
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserDnPatterns("uid={0},ou=people");
return factory.createAuthenticationManager();
}

前面的简单示例会通过将用户登录名替换到提供的模式中,并尝试使用登录密码作为该用户进行绑定来获取用户的DN。如果您的所有用户都存储在目录的单个节点下,这是可行的。相反,如果您希望配置一个LDAP搜索过滤器来定位用户,可以使用以下方法:

@Bean
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserSearchFilter("(uid={0})");
factory.setUserSearchBase("ou=people");
return factory.createAuthenticationManager();
}

如果与之前展示的 ContextSource 定义 结合使用,这将通过过滤器 (uid={0}) 在 DN ou=people,dc=springframework,dc=org 下执行搜索。同样,用户登录名会替换过滤器名称中的参数,因此它会搜索 uid 属性等于用户名的条目。如果未提供用户搜索基础,则从根目录开始执行搜索。

使用密码认证

密码比对是指将用户提供的密码与存储在存储库中的密码进行比较。这可以通过两种方式实现:一是检索密码属性的值并在本地进行校验;二是执行LDAP“比较”操作,将提供的密码传递给服务器进行比对,且真实密码值永远不会被检索。当密码使用随机盐值进行适当哈希处理时,则无法执行LDAP比较操作。

@Bean
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
LdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(
contextSource, NoOpPasswordEncoder.getInstance());
factory.setUserDnPatterns("uid={0},ou=people");
return factory.createAuthenticationManager();
}

以下示例展示了一个包含一些自定义配置的更高级设置:

@Bean
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
LdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(
contextSource, new BCryptPasswordEncoder());
factory.setUserDnPatterns("uid={0},ou=people");
factory.setPasswordAttribute("pwd"); 1
return factory.createAuthenticationManager();
}
  • 将密码属性指定为 pwd

LdapAuthoritiesPopulator

Spring Security 的 LdapAuthoritiesPopulator 用于确定返回给用户的权限。以下示例展示了如何配置 LdapAuthoritiesPopulator

@Bean
LdapAuthoritiesPopulator authorities(BaseLdapPathContextSource contextSource) {
String groupSearchBase = "";
DefaultLdapAuthoritiesPopulator authorities =
new DefaultLdapAuthoritiesPopulator(contextSource, groupSearchBase);
authorities.setGroupSearchFilter("member={0}");
return authorities;
}

@Bean
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource, LdapAuthoritiesPopulator authorities) {
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserDnPatterns("uid={0},ou=people");
factory.setLdapAuthoritiesPopulator(authorities);
return factory.createAuthenticationManager();
}

Active Directory

Active Directory 支持其自身的非标准身份验证选项,其常规使用模式与标准的 LdapAuthenticationProvider 并不完全契合。通常,身份验证是通过使用域用户名(格式为 user@domain)来执行的,而不是使用 LDAP 可分辨名称。为了简化这一过程,Spring Security 提供了一个专门针对典型 Active Directory 设置进行定制的身份验证提供程序。

配置 ActiveDirectoryLdapAuthenticationProvider 相当简单。您只需提供域名和一个包含服务器地址的 LDAP URL。

备注

也可以通过 DNS 查询获取服务器的 IP 地址。目前暂不支持此功能,但希望未来版本能够实现。

以下示例配置了 Active Directory:

@Bean
ActiveDirectoryLdapAuthenticationProvider authenticationProvider() {
return new ActiveDirectoryLdapAuthenticationProvider("example.com", "ldap://company.example.com/");
}