文章目录
验证用户身份最常见的方法之一是验证用户名和密码。因此,Spring Security为使用用户名和密码进行身份验证提供了全面的支持。Spring Security为从HttpServletRequest读取用户名和密码提供了三种内置机制:Form、Basic、Digest。
Form Login
Spring Security支持通过html表单提供用户名和密码。本节详细介绍了基于表单的身份验证如何在Spring Security中工作。
让我们看看Spring Security中基于表单的登录是如何工作的。首先,我们看到用户是如何重定向到登录表单的。
该图构建了我们的SpringSecurityFilterChain图:
- 首先,用户向未经授权的资源/私有资源发出未经验证的请求。
- Spring Security的FilterSecurityInterceptor通过抛出AccessDeniedException指示未经验证的请求被拒绝。
- 由于用户未经身份验证,ExceptionTranslationFilter启动启动身份验证,并使用配置的AuthenticationEntryPoint向登录页面发送重定向。在大多数情况下,AuthenticationEntryPoint是LoginUrauThenticationEntryPoint的一个实例。
- 浏览器将请求重定向到的登录页面。
加载登录页面,提交用户名和密码时,UsernamePasswordAuthenticationFilter会对用户名和密码进行身份验证。UsernamePasswordAuthenticationFilter扩展了AbstractAuthenticationProcessingFilter,所以看起来应该是这个图:
- 当用户提交用户名和密码时,UsernamePasswordAuthenticationFilter会创建一个UsernamePasswordAuthenticationToken,这是一种通过从HttpServletRequest中提取用户名和密码进行身份验证的类型。
- 接下来,UsernamePasswordAuthenticationToken被传递到AuthenticationManager进行身份验证。AuthenticationManager认证细节取决于用户信息的存储方式。
- 如果身份验证失败,则:
(1)SecurityContextHolder被清除。
(2)调用RememberMeServices.loginFail,如果“remember me”未配置,则不需要操作。
(3)调用AuthenticationFailureHandler。 - 如果身份验证成功,则:
(1)SessionAuthenticationStrategy会收到新登录的通知。
(2)将Authentication设置到SecurityContextHolder。
(3)调用RememberMeServices.loginSuccess ,如果“remember me”未配置,则不需要操作。
(4)ApplicationEventPublisher发布一个InteractiveAuthenticationSuccessEvent。
(5)调用AuthenticationSuccessHandler。通常这是一个SimpleRuthenticationSuccessHandler,当我们重定向到登录页面时,它将重定向到ExceptionTranslationFilter保存的请求。
默认情况下启用Spring安全表单登录。然而一旦提供了任何基于servlet的配置,就必须显式地提供登录表单。下面可以找到一个最小的显式Java配置:
protected void configure(HttpSecurity http) {
http
// ...
.formLogin(withDefaults());
}
在此配置中,Spring Security将呈现一个默认的登录页面。大多数生产应用程序都需要自定义登录表单。下面的配置演示了如何提供自定义登录表单:
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.formLogin(form -> form
.loginPage("/login")
.permitAll()
);
}
当在SpringSecurity中指定登录页面时,您负责呈现该页面。下面是一个Thymeleaf模板,它生成一个HTML登录表单,该表单符合/login的登录页面:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
<title>Please Log In</title>
</head>
<body>
<h1>Please Log In</h1>
<div th:if="${param.error}">
Invalid username and password.</div>
<div th:if="${param.logout}">
You have been logged out.</div>
<form th:action="@{/login}" method="post">
<div>
<input type="text" name="username" placeholder="Username"/>
</div>
<div>
<input type="password" name="password" placeholder="Password"/>
</div>
<input type="submit" value="Log in" />
</form>
</body>
</html>
关于默认HTML表单,有几个关键点:
- 表单应该使用post 调用/login接口
- 表单需要包含一个CSRF令牌,该令牌由Thymeleaf自动包含。
- 表单应该在名为username的参数中指定用户名
- 表单应在名为password的参数中指定密码
- 如果发现HTTP参数错误,则表示用户未能提供有效的用户名/密码 如果找到HTTP参数logout,则表示用户已成功注销。
许多用户只需定制登录页面即可。但是,如果需要,可以通过其他配置定制上述所有内容。
Basic Authentication
本节详细介绍了Spring Security如何为基于servlet的应用程序提供基本HTTP身份验证支持。
让我们看看HTTP基本身份验证在Spring Security中是如何工作的。首先,我们看到WWW Authenticate头被发送回未经身份验证的客户端。
- 首先,用户向未经授权的资源/私有资源发出未经验证的请求。
- Spring Security的FilterSecurity Interceptor通过抛出AccessDeniedException指示未经验证的请求被拒绝。
- 由于用户未经身份验证,ExceptionTranslationFilter会启动启动身份验证。配置的AuthenticationEntryPoint是BasicAuthenticationEntryPoint的一个实例,它发送一个WWW Authentication头。RequestCache通常是不保存请求的NullRequestCache,因为客户端能够重放它最初的请求。
当客户端收到WWW Authenticate标头时,它知道应该使用用户名和密码重试。下面是正在处理的用户名和密码的流程:
- 当用户提交用户名和密码时,BasicAuthenticationFilter会创建一个UsernamePasswordAuthenticationToken,这是一种通过从HttpServlet请求中提取用户名和密码进行身份验证的类型。
- 其次,UsernamePasswordAuthenticationToken被传递到AuthenticationManager进行身份验证。AuthenticationManager认证的细节取决于用户信息的存储方式。
- 如果身份验证失败,则:
(1)SecurityContextHolder被清除。
(2)调用RememberMeServices.loginFail,如果“remember me”未配置,则不需要操作。
(3)AuthenticationEntryPoint被调用以触发再次发送WWW身份验证。 - 如果身份验证成功,则:
(1)将Authentication设置到SecurityContextHolder。
(2)调用RememberMeServices.loginSuccess ,如果“remember me”未配置,则不需要操作。
(3)BasicAuthenticationFilter调用FilterChain.doFilter(request,response) 继续应用程序逻辑的其余部分。
默认情况下,Spring Security在中的HTTP基本身份验证支持处于启用状态。然而,一旦提供了任何基于servlet的配置,就必须显式地提供HTTP Basic。下面可以找到一个最小的显式配置:
protected void configure(HttpSecurity http) {
http
// ...
.httpBasic(withDefaults());
}
Digest Authentication
本节详细介绍了Spring Security如何支持DigestAuthenticationFilter提供的Digest Authentication。
警告:您不应该在现在的应用程序中使用Digest Authentication,因为它被认为是不安全的。最明显的问题是,您必须以明文、加密或MD5格式存储密码。所有这些存储格式都被认为是不安全的。相反,您应该使用Digest Authentication不支持的单向自适应密码哈希(即bCrypt、PBKDF2、SCrypt等)存储凭据。
Digest Authentication试图解决Basic Authentication的许多弱点,特别是通过确保credentials永远不会以明文形式通过网络发送。许多浏览器支持摘要身份验证。
管理HTTP Digest Authentication的标准由RFC 2617定义,它更新了RFC 2069规定的摘要认证标准的早期版本。大多数用户实现RFC2617.大多数用户代理实现RFC2617。Spring Security的Digest Authentication支持与RFC 2617规定的"auth” quality of protection (qop) 兼容,它还提供了与RFC 2069的向后兼容性。如果您需要使用未加密的HTTP(即无TLS/HTTPS)并希望最大限度地提高身份验证过程的安全性,则Digest Authentication被视为一个更具吸引力的选项。然而,每个人都应该使用HTTPS。
Digest Authentication的核心是“nonce(随机数)”。这是服务器生成的值。Spring Security的nonce采用以下格式:
base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))
expirationTime: The date and time when the nonce expires, expressed in milliseconds
key: A private key to prevent modification of the nonce token
您需要确保使用NoOpPasswordEncoder配置纯文本密码存储。下面提供了使用Java配置配置摘要身份验证的示例:
@Autowired
UserDetailsService userDetailsService;
DigestAuthenticationEntryPoint entryPoint() {
DigestAuthenticationEntryPoint result = new DigestAuthenticationEntryPoint();
result.setRealmName("My App Relam");
result.setKey("3028472b-da34-4501-bfd8-a355c42bdf92");
}
DigestAuthenticationFilter digestAuthenticationFilter() {
DigestAuthenticationFilter result = new DigestAuthenticationFilter();
result.setUserDetailsService(userDetailsService);
result.setAuthenticationEntryPoint(entryPoint());
}
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.exceptionHandling(e -> e.authenticationEntryPoint(authenticationEntryPoint()))
.addFilterBefore(digestFilter());
}
Password Storage
每种读取用户名和密码的机制都可以找到对应的支持:
- Simple Storage with In-Memory Authentication
- Relational Databases with JDBC Authentication
- Custom data stores with UserDetailsService
- LDAP storage with LDAP Authentication
In-Memory Authentication
Spring Security的InMemoryUserDetailsManager实现UserDetailsService,为存储在内存中的基于用户名/密码的身份验证提供支持。InMemoryUserDetailsManager通过实现UserDetailsManager接口提供对UserDetails的管理。当Spring Security配置为接受用户名/密码进行身份验证时,会使用基于UserDetails的身份验证。
这个示例中,我们使用Spring Boot对password的密码进行编码,并获取password的编码密码{bcrypt}$2a
10
10
10GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW.
@Bean
public UserDetailsService users() {
UserDetails user = User.builder()
.username("user")
.password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
.roles("USER", "ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
上面的示例以安全的格式存储密码,但在入门体验方面还有很多需要改进的地方。在下面的示例中,我们利用了用户。使用DefaultPasswordEncoder确保存储在内存中的密码受到保护。但是,它不能防止通过反编译源代码获得密码。因此,用户。withDefaultPasswordEncoder应仅用于“入门”,不用于生产。
@Bean
public UserDetailsService users() {
// The builder will ensure the passwords are encoded before saving in memory
UserBuilder users = User.withDefaultPasswordEncoder();
UserDetails user = users
.username("user")
.password("password")
.roles("USER")
.build();
UserDetails admin = users
.username("admin")
.password("password")
.roles("USER", "ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
没有基于XML配置简便使用User.withDefaultPasswordEncoder的方式。对于演示或刚刚开始,您可以选择在密码前面加上{noop}以指示不应使用编码。
<user-service>
<user name="user"
password="{noop}password"
authorities="ROLE_USER" />
<user name="admin"
password="{noop}password"
authorities="ROLE_USER,ROLE_ADMIN" />
</user-service>
JDBC Authentication
Spring Security的JdbcDaoImpl实现UserDetailsService,为使用JDBC检索的基于用户名/密码的身份验证提供支持。JdbcUserDetailsManager扩展了JdbcDaoImpl,通过UserDetailsManager接口提供对UserDetails的管理。当Spring Security配置为接受用户名/密码进行身份验证时,会使用基于UserDetails的身份验证。
下面我们将要讨论:
- Spring Security JDBC Authentication使用的默认方案 Default Schema
- 设置数据源
- JdbcUserDetailsManager Bean
Default Schema
Spring Security为基于JDBC的身份验证提供默认查询。本节提供与默认查询一起使用的相应默认模式。您需要调整模式,使其与查询和正在使用的数据库方言的任何定制相匹配。
User Schema
JdbcDaoImpl需要表来加载用户的密码、帐户状态(已启用或已禁用)和权限列表(角色)。下面可以找到所需的默认模式:默认模式也作为名为org/springframework/security/core/userdetails/jdbc/users.ddl。
create table users(
username varchar_ignorecase(50) not null primary key,
password varchar_ignorecase(500) not null,
enabled boolean not null
);
create table authorities (
username varchar_ignorecase(50) not null,
authority varchar_ignorecase(50) not null,
constraint fk_authorities_users foreign key(username) references users(username)
);
create unique index ix_auth_username on authorities (username,authority);
Oracle是一种流行的数据库选择,但需要稍微不同的模式。您可以在下面找到用户的默认Oracle模式:
CREATE TABLE USERS (
USERNAME NVARCHAR2(128) PRIMARY KEY,
PASSWORD NVARCHAR2(128) NOT NULL,
ENABLED CHAR(1) CHECK (ENABLED IN ('Y','N') ) NOT NULL
);
CREATE TABLE AUTHORITIES (
USERNAME NVARCHAR2(128) NOT NULL,
AUTHORITY NVARCHAR2(128) NOT NULL
);
ALTER TABLE AUTHORITIES ADD CONSTRAINT AUTHORITIES_UNIQUE UNIQUE (USERNAME, AUTHORITY);
ALTER TABLE AUTHORITIES ADD CONSTRAINT AUTHORITIES_FK1 FOREIGN KEY (USERNAME) REFERENCES USERS (USERNAME) ENABLE;
Group Schema
如果应用程序正在利用groups,则需要提供groups schema(分组方案)。下面可以找到组的默认模式。
create table groups (
id bigint generated by default as identity(start with 0) primary key,
group_name varchar_ignorecase(50) not null
);
create table group_authorities (
group_id bigint not null,
authority varchar(50) not null,
constraint fk_group_authorities_group foreign key(group_id) references groups(id)
);
create table group_members (
id bigint generated by default as identity(start with 0) primary key,
username varchar(50) not null,
group_id bigint not null,
constraint fk_group_members_group foreign key(group_id) references groups(id)
);
Setting up a DataSource
在配置JdbcUserDetailsManager之前,我们必须创建一个数据源。在我们的示例中,我们将设置一个用默认用户模式初始化的嵌入式数据源。
@Bean
DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(H2)
.addScript("classpath:org/springframework/security/core/userdetails/jdbc/users.ddl")
.build();
}
在生产环境中,您需要确保设置到外部数据库的连接。
JdbcUserDetailsManager Bean
在这个示例中,我们使用Spring Boot CLI对password的密码进行编码,并获得{bcrypt}$2a 10 10 10GRLdNijSQMUvl/au9ofL.Edwmoohzz7.rmNSJZ。0FxO/BTk76klW的编码密码。有关如何存储密码的更多详细信息,请参阅PasswordEncoder部分。
@Bean
UserDetailsManager users(DataSource dataSource) {
UserDetails user = User.builder()
.username("user")
.password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
.roles("USER", "ADMIN")
.build();
JdbcUserDetailsManager users = new JdbcUserDetailsManager(dataSource);
users.createUser(user);
users.createUser(admin);
return users;
}
UserDetails
UserDetails由UserDetailsService返回。DaoAuthenticationProvider验证UserDetails,然后返回一个包含principal 的Authentication ,该Authentication 的principal是已配置的UserDetails服务返回的UserDetails。
UserDetailsService
UserDetailsService由DaoAuthenticationProvider用于检索用户名、密码和其他属性,以便使用用户名和密码进行身份验证。Spring Security提供了UserDetailsService的内存和JDBC实现。
可以通过将自定义UserDetailsService公开为bean来定义自定义身份验证。例如,假设CustomUserDetailsService实现了UserDetailsService,以下内容将自定义身份验证(仅当尚未填充AuthenticationManagerBuilder且未定义AuthenticationProviderBean时,才使用此选项):
@Bean
CustomUserDetailsService customUserDetailsService() {
return new CustomUserDetailsService();
}
PasswordEncoder
Spring Security的servlet支持通过与PasswordEncoder集成来安全地存储密码。定制Spring Security使用的PasswordEncoder实现可以通过公开PasswordEncoder Bean来完成。
DaoAuthenticationProvider
DaoAuthenticationProvider是一个AuthenticationProvider实现,它利用UserDetails服务和PasswordEncoder来验证用户名和密码。
让我们看看DaoAuthenticationProvider在Spring Security中是如何工作的。该图通过读取用户名和密码详细解释了AuthenticationManager在图中的工作方式。
- Authentication Filter会将读取到的用户名和密码封装成UsernamePasswordAuthenticationToken传递给由ProviderManager实现的AuthenticationManager。
- ProviderManager配置为使用DaoAuthenticationProvider类型的AuthenticationProvider。
- DaoAuthenticationProvider从UserDetails服务中查找UserDetails。
- DaoAuthenticationProvider然后使用PasswordEncoder在上一步返回的UserDetails上验证密码。
- 身份验证成功后,返回的身份验证类型为UsernamePasswordAuthenticationToken,其principal 是配置的UserDetailsService返回的UserDetails。最终, Authentication Filter将在SecurityContextHolder上设置返回的UsernamePasswordAuthenticationToken。
LDAP Authentication
LDAP通常被组织用作用户信息的中央存储库和身份验证服务。它还可以用于存储应用程序用户的角色信息。
当配置为接受用户名/密码进行身份验证时,Spring Security将使用Spring Security基于LDAP的身份验证。然而,尽管利用用户名/密码进行身份验证,但它没有使用UserDetailsService进行集成,因为在绑定身份验证中,LDAP服务器不会返回密码,因此应用程序无法执行密码验证。
对于如何配置LDAP服务器,有许多不同的场景,因此Spring Security的LDAP提供程序是完全可配置的。它使用单独的策略接口进行身份验证和角色检索,并提供可配置为处理各种情况的默认实现。
Prerequisites(先决条件)
在尝试将LDAP与Spring Security结合使用之前,您应该熟悉LDAP。以下链接很好地介绍了相关概念,并提供了使用免费LDAP服务器OpenLDAP设置目录的指南:https://www.zytrax.com/books/ldap/.熟悉用于从Java访问LDAP的JNDIAPI可能也很有用。我们在LDAP提供程序中不使用任何第三方LDAP库(Mozilla、JLDAP等),但广泛使用Spring LDAP,因此如果您计划添加自己的定制,那么对该项目的一些熟悉可能会很有用。
使用LDAP身份验证时,确保正确配置LDAP连接池非常重要。如果您不熟悉如何做到这一点,可以参考Java LDAP文档。
Setting up an Embedded LDAP Server
您需要做的第一件事是确保您有一个LDAP服务器来指向您的配置。为了简单起见,通常最好从嵌入式LDAP服务器开始。Spring Security支持使用以下任一选项:
- Embedded UnboundID Server
- Embedded ApacheDS Server
在下面的示例中,我们将以下内容公开为用户。ldif作为一个类路径资源,用用户user和admin初始化嵌入式LDAP服务器,这两个用户的密码都是password。
users.ldif
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
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org
uniqueMember: uid=user,ou=people,dc=springframework,dc=org
dn: cn=admin,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: admin
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org
Embedded UnboundID Server
如果希望使用Unbounded,请指定以下依赖项:
<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
<version>4.0.14</version>
<scope>runtime</scope>
</dependency>
@Bean
UnboundIdContainer ldapContainer() {
return new UnboundIdContainer("dc=springframework,dc=org",
"classpath:users.ldif");
}
Embedded ApacheDS Server
Spring Security使用ApacheDS1.x不再维护的,不幸的是,ApacheDS 2.x只发布了里程碑版本,没有稳定的版本。一旦ApacheDS 2.x稳定发布我们会考虑更新。
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-core</artifactId>
<version>1.5.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-server-jndi</artifactId>
<version>1.5.5</version>
<scope>runtime</scope>
</dependency>
@Bean
ApacheDSContainer ldapContainer() {
return new ApacheDSContainer("dc=springframework,dc=org",
"classpath:users.ldif");
}
LDAP ContextSource
一旦您有了一个LDAP服务器来指向您的配置,您就需要配置Spring Security来指向一个应该用来验证用户身份的LDAP服务器。这是通过创建LDAP ContextSource来实现的,它相当于JDBC数据源。
ContextSource contextSource(UnboundIdContainer container) {
return new DefaultSpringSecurityContextSource("ldap://localhost:53389/dc=springframework,dc=org");
}
Authentication
Spring Security的LDAP支持不使用UserDetails服务,因为LDAP绑定身份验证不允许客户端读取密码,甚至不允许客户端读取密码的哈希版本。这意味着无法读取密码,然后由Spring Security进行身份验证。
因此,LDAP支持是使用LdapAuthenticator接口实现的。LdapAuthenticator还负责检索任何必需的用户属性。这是因为属性上的权限可能取决于所使用的身份验证类型。例如,如果以用户身份绑定,可能需要使用用户自己的权限读取它们。
Spring Security提供了两种LdapAuthenticator实现:
- Using Bind Authentication
- Using Password Authentication
Using Bind Authentication
绑定身份验证是使用LDAP对用户进行身份验证的最常见机制。在绑定身份验证中,用户凭据(即用户名/密码)被提交到LDAP服务器,LDAP服务器对用户进行身份验证。使用绑定身份验证的优点是,用户的秘密(即密码)不需要向客户端公开,这有助于保护它们不被泄露。
下面是绑定身份验证配置的示例。
@Bean
BindAuthenticator authenticator(BaseLdapPathContextSource contextSource) {
BindAuthenticator authenticator = new BindAuthenticator(contextSource);
authenticator.setUserDnPatterns(new String[] { "uid={0},ou=people" });
return authenticator;
}
@Bean
LdapAuthenticationProvider authenticationProvider(LdapAuthenticator authenticator) {
return new LdapAuthenticationProvider(authenticator);
}
这个简单的示例将通过在提供的模式中替换用户登录名并尝试用登录密码绑定为该用户来获取该用户的DN。如果所有用户都存储在目录中的一个节点下,则这是正常的。如果希望配置LDAP搜索筛选器来定位用户,可以使用以下选项:
@Bean
BindAuthenticator authenticator(BaseLdapPathContextSource contextSource) {
String searchBase = "ou=people";
String filter = "(uid={0})";
FilterBasedLdapUserSearch search =
new FilterBasedLdapUserSearch(searchBase, filter, contextSource);
BindAuthenticator authenticator = new BindAuthenticator(contextSource);
authenticator.setUserSearch(search);
return authenticator;
}
@Bean
LdapAuthenticationProvider authenticationProvider(LdapAuthenticator authenticator) {
return new LdapAuthenticationProvider(authenticator);
}
如果与上面的ContextSource定义一起使用,这将在DN ou=people、dc=springframework、dc=org下使用(uid={0})作为过滤器执行搜索。同样,用户登录名将替换过滤器名称中的参数,因此它将搜索uid属性等于用户名的条目。如果未提供用户搜索库,则将从根目录执行搜索。
Using Password Authentication
密码比较是将用户提供的密码与存储库中存储的密码进行比较。这可以通过检索password属性的值并在本地进行检查来实现,也可以通过执行LDAP“比较”操作来实现,其中提供的密码被传递给服务器进行比较,而真正的密码值永远不会被检索。如果密码用随机盐正确散列,则无法进行LDAP比较.
@Bean
PasswordComparisonAuthenticator authenticator(BaseLdapPathContextSource contextSource) {
return new PasswordComparisonAuthenticator(contextSource);
}
@Bean
LdapAuthenticationProvider authenticationProvider(LdapAuthenticator authenticator) {
return new LdapAuthenticationProvider(authenticator);
}
下面可以找到一些定制的更高级配置。
@Bean
PasswordComparisonAuthenticator authenticator(BaseLdapPathContextSource contextSource) {
PasswordComparisonAuthenticator authenticator =
new PasswordComparisonAuthenticator(contextSource);
// 将密码属性指定为pwd
authenticator.setPasswordAttributeName("pwd");
// 使用BCryptPasswordEncoder
authenticator.setPasswordEncoder(new BCryptPasswordEncoder());
return authenticator;
}
@Bean
LdapAuthenticationProvider authenticationProvider(LdapAuthenticator authenticator) {
return new LdapAuthenticationProvider(authenticator);
}
LdapAuthoritiesPopulator(LDAP权限填充器)
Spring Security的LdapAuthoritiesPopulator用于确定为用户返回哪些授权。
@Bean
LdapAuthoritiesPopulator authorities(BaseLdapPathContextSource contextSource) {
String groupSearchBase = "";
DefaultLdapAuthoritiesPopulator authorities =
new DefaultLdapAuthoritiesPopulator(contextSource, groupSearchBase);
authorities.setGroupSearchFilter("member={0}");
return authorities;
}
@Bean
LdapAuthenticationProvider authenticationProvider(LdapAuthenticator authenticator, LdapAuthoritiesPopulator authorities) {
return new LdapAuthenticationProvider(authenticator, authorities);
}
Active Directory
Active Directory支持自己的非标准身份验证选项,而正常的使用模式不太适合标准的LdapAuthenticationProvider。通常,使用域用户名(格式为user@domain),而不是使用LDAP可分辨名称。为了简化这一过程,Spring Security提供了一个身份验证提供程序,它是为典型的Active Directory设置定制的。
配置ActiveDirectoryLdapAuthenticationProvider非常简单。您只需要提供域名和一个提供服务器地址的LDAP URL[1]。下面可以看到一个配置示例:
@Bean
ActiveDirectoryLdapAuthenticationProvider authenticationProvider() {
return new ActiveDirectoryLdapAuthenticationProvider("example.com", "ldap://company.example.com/");
}