对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

概述

记住我或持久登录身份验证是指网站能够在会话之间记住主体的身份。 这通常是通过向浏览器发送 cookie 来实现的,在将来的会话中检测到 cookie 并导致自动登录。 Spring Security 为这些操作的发生提供了必要的钩子,并且有两个具体的 remember-me 实现。 一种使用哈希来保护基于 cookie 的令牌的安全性,另一种使用数据库或其他持久性存储机制来存储生成的令牌。Spring中文文档

请注意,这两种实现都需要 . 如果您使用的是不使用 (例如,LDAP 提供程序)的身份验证提供程序,那么除非应用程序上下文中也有一个 bean,否则它将无法工作。UserDetailsServiceUserDetailsServiceUserDetailsServiceSpring中文文档

简单的基于哈希的令牌方法

此方法使用哈希来实现有用的“记住我”策略。 从本质上讲,在交互式身份验证成功后,cookie 会发送到浏览器,cookie 的组成如下:Spring中文文档

base64(username + ":" + expirationTime + ":" + algorithmName + ":"
algorithmHex(username + ":" + expirationTime + ":" password + ":" + key))

username:          As identifiable to the UserDetailsService
password:          That matches the one in the retrieved UserDetails
expirationTime:    The date and time when the remember-me token expires, expressed in milliseconds
key:               A private key to prevent modification of the remember-me token
algorithmName:     The algorithm used to generate and to verify the remember-me token signature

因此,记住我令牌仅在指定的时间段内有效,并且用户名、密码和密钥不会更改。 值得注意的是,这存在一个潜在的安全问题,因为在令牌过期之前,捕获的记住我令牌可以从任何用户代理使用。 这与摘要式身份验证的问题相同。 如果委托人知道令牌已被捕获,他们可以轻松更改其密码,并立即使所有有问题的记住我令牌失效。 如果需要更重要的安全性,则应使用下一节中介绍的方法。 或者,根本不应该使用记住我服务。Spring中文文档

如果您熟悉有关命名空间配置的章节中讨论的主题,则只需添加以下元素即可启用记住我身份验证:<remember-me>Spring中文文档

<http>
...
<remember-me key="myAppKey"/>
</http>

通常会自动选择。 如果应用程序上下文中有多个,则需要指定应将哪个属性与属性一起使用,其中值是 Bean 的名称。UserDetailsServiceuser-service-refUserDetailsServiceSpring中文文档

持久令牌方法

此方法基于文章改进的持久登录 Cookie 最佳实践,并进行了一些小的修改[1]. 若要将此方法用于命名空间配置,请提供数据源引用:Spring中文文档

<http>
...
<remember-me data-source-ref="someDataSource"/>
</http>

数据库应包含使用以下 SQL(或等效项)创建的表:persistent_loginsSpring中文文档

create table persistent_logins (username varchar(64) not null,
								series varchar(64) primary key,
								token varchar(64) not null,
								last_used timestamp not null)

Remember-Me 接口和实现

Remember-me 与 一起使用,并通过超类中的钩子实现。 它也用于 . 钩子将在适当的时候调用混凝土。 界面如下所示:UsernamePasswordAuthenticationFilterAbstractAuthenticationProcessingFilterBasicAuthenticationFilterRememberMeServicesSpring中文文档

Authentication autoLogin(HttpServletRequest request, HttpServletResponse response);

void loginFail(HttpServletRequest request, HttpServletResponse response);

void loginSuccess(HttpServletRequest request, HttpServletResponse response,
	Authentication successfulAuthentication);

请参考 Javadoc 来更全面地讨论这些方法的作用,尽管请注意,在这个阶段只调用 and 方法。 每当 不包含 . 因此,此接口为底层 remember-me 实现提供了与身份验证相关的事件的充分通知,并在候选 Web 请求可能包含 cookie 并希望被记住时委托给实现。 此设计允许任意数量的 remember-me 实现策略。 我们在上面已经看到,Spring Security 提供了两种实现。 我们将依次查看这些内容。AbstractAuthenticationProcessingFilterloginFail()loginSuccess()autoLogin()RememberMeAuthenticationFilterSecurityContextHolderAuthenticationSpring中文文档

TokenBasedRememberMe服务

此实现支持简单基于哈希的令牌方法中所述的更简单方法。 生成 ,由 处理。 A 在此身份验证提供程序和 . 此外,还需要 UserDetailsService,它可以从中检索用户名和密码以进行签名比较,并生成包含正确的 s。 如果用户请求,应用程序应提供某种注销命令,该命令会使 cookie 失效。 还实现了Spring Security的界面,因此可以用于自动清除cookie。TokenBasedRememberMeServicesRememberMeAuthenticationTokenRememberMeAuthenticationProviderkeyTokenBasedRememberMeServicesTokenBasedRememberMeServicesRememberMeAuthenticationTokenGrantedAuthorityTokenBasedRememberMeServicesLogoutHandlerLogoutFilterSpring中文文档

默认情况下,此实现使用 MD5 算法对令牌签名进行编码。 为了验证令牌签名,将分析并使用从中检索到的算法。 如果不存在,则将使用默认匹配算法,即 MD5。 您可以为签名编码和签名匹配指定不同的算法,这允许用户安全地升级到不同的编码算法,同时仍然能够验证旧算法(如果不存在)。 为此,您可以将自定义指定为 Bean 并在配置中使用它。algorithmNamealgorithmNamealgorithmNameTokenBasedRememberMeServicesSpring中文文档

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http, RememberMeServices rememberMeServices) throws Exception {
	http
			.authorizeHttpRequests((authorize) -> authorize
					.anyRequest().authenticated()
			)
			.rememberMe((remember) -> remember
				.rememberMeServices(rememberMeServices)
			);
	return http.build();
}

@Bean
RememberMeServices rememberMeServices(UserDetailsService userDetailsService) {
	RememberMeTokenAlgorithm encodingAlgorithm = RememberMeTokenAlgorithm.SHA256;
	TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(myKey, userDetailsService, encodingAlgorithm);
	rememberMe.setMatchingAlgorithm(RememberMeTokenAlgorithm.MD5);
	return rememberMe;
}
<http>
  <remember-me services-ref="rememberMeServices"/>
</http>

<bean id="rememberMeServices" class=
"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
    <property name="userDetailsService" ref="myUserDetailsService"/>
    <property name="key" value="springRocks"/>
    <property name="matchingAlgorithm" value="MD5"/>
    <property name="encodingAlgorithm" value="SHA256"/>
</bean>

在应用程序上下文中需要以下 Bean 才能启用 remember-me 服务:Spring中文文档

@Bean
RememberMeAuthenticationFilter rememberMeFilter() {
    RememberMeAuthenticationFilter rememberMeFilter = new RememberMeAuthenticationFilter();
    rememberMeFilter.setRememberMeServices(rememberMeServices());
    rememberMeFilter.setAuthenticationManager(theAuthenticationManager);
    return rememberMeFilter;
}

@Bean
TokenBasedRememberMeServices rememberMeServices() {
    TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices();
    rememberMeServices.setUserDetailsService(myUserDetailsService);
    rememberMeServices.setKey("springRocks");
    return rememberMeServices;
}

@Bean
RememberMeAuthenticationProvider rememberMeAuthenticationProvider() {
    RememberMeAuthenticationProvider rememberMeAuthenticationProvider = new RememberMeAuthenticationProvider();
    rememberMeAuthenticationProvider.setKey("springRocks");
    return rememberMeAuthenticationProvider;
}
<bean id="rememberMeFilter" class=
"org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
<property name="rememberMeServices" ref="rememberMeServices"/>
<property name="authenticationManager" ref="theAuthenticationManager" />
</bean>

<bean id="rememberMeServices" class=
"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="myUserDetailsService"/>
<property name="key" value="springRocks"/>
</bean>

<bean id="rememberMeAuthenticationProvider" class=
"org.springframework.security.authentication.RememberMeAuthenticationProvider">
<property name="key" value="springRocks"/>
</bean>

不要忘记将您的实现添加到您的属性中,将 包含在您的列表中,并添加到您的(通常紧接在您的 之后)。RememberMeServicesUsernamePasswordAuthenticationFilter.setRememberMeServices()RememberMeAuthenticationProviderAuthenticationManager.setProviders()RememberMeAuthenticationFilterFilterChainProxyUsernamePasswordAuthenticationFilterSpring中文文档

PersistentTokenBasedRememberMeServices

您可以按照与 相同的方式使用此类,但还需要使用 a 对其进行配置以存储令牌。TokenBasedRememberMeServicesPersistentTokenRepositorySpring中文文档

上面的持久令牌方法中介绍了数据库架构。Spring中文文档


1.从本质上讲,用户名不包含在cookie中,以防止不必要地暴露有效的登录名。本文的评论部分对此进行了讨论。