记住我(Remember-Me) 认证
本站(springdoc.cn)中的内容来源于 spring.io ,原始版权归属于 spring.io。由 springdoc.cn 进行翻译,整理。可供个人学习、研究,未经许可,不得进行任何转载、商用或与之相关的行为。 商标声明:Spring 是 Pivotal Software, Inc. 在美国以及其他国家的商标。 |
Remember-me(记住我)或persistent-login认证是指网站能够在两次会话之间记住委托人的身份。这通常是通过向浏览器发送一个cookie来实现的,在未来的会话中可以检测到cookie,并导致自动登录的发生。Spring Security为这些操作提供了必要的钩子,并有两个具体的“记住我”的实现。一个使用散列法来保护基于cookie的令牌的安全性,另一个使用数据库或其他持久性存储机制来存储生成的令牌。
请注意,这两种实现都需要一个 UserDetailsService
。如果你使用一个不使用 UserDetailsService
的认证 provider(例如,LDAP provider),除非你在你的应用程序上下文中也有一个 UserDetailsService
Bean,否则它不会工作。
基于哈希简单令牌法
这种方法使用散列法来实现有用的“记住我”的策略。实质上,在成功的交互式认证后,会向浏览器发送一个cookie,cookie的组成如下。
base64(username + ":" + expirationTime + ":" + algorithmName + ":"
algorithmHex(username + ":" + expirationTime + ":" password + ":" + key))
username: UserDetailsService 的 ID
password: 这与检索到的UserDetails中的内容相匹配。
expirationTime: 令牌过期的日期和时间,以毫秒表示。
key: 用于防止修改“记住我”令牌的私钥
algorithmName: 用于生成和验证 “记住我” 令牌签名的算法。
“remember-me” 令牌只在指定的时间内有效,并且只有在用户名、密码和密钥不改变的情况下才有效。值得注意的是,这有一个潜在的安全问题,因为捕获的“remember-me”令牌可以从任何用户代理处使用,直到令牌过期。这与摘要认证的问题相同。如果一个委托人意识到一个令牌已经被泄露,他们可以很容易地改变他们的密码,并立即使所有发行的“remember-me”令牌失效。如果需要更重要的安全性,你应该使用下一节中描述的方法。或者,根本就不应该使用 “remember-me” 服务。
如果你熟悉 命名空间配置一章中讨论的主题,你可以通过添加 <remember-me>
元素来启用 remember-me 认证。
<http>
...
<remember-me key="myAppKey"/>
</http>
UserDetailsService
通常会被自动选择。如果你的 application context 中有多个,你需要用 user-service-ref
属性指定哪一个,其中的值是你的 UserDetailsService
bean的名称。
持久化令牌法
这种方法是基于标题为 http://jaspan.com/improved_persistent_login_cookie_best_practice,的文章,并做了一些小的修改。(基本上,用户名不包括在cookie中,以防止不必要地暴露一个有效的登录名。在这篇文章的评论部分有关于这个问题的讨论)。要使用这种命名空间配置的方法,需要提供一个数据源参考。
<http>
...
<remember-me data-source-ref="someDataSource"/>
</http>
数据库应该包含一个 persistent_logins
表,通过使用以下SQL语句(或类似语句)创建。
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与 UsernamePasswordAuthenticationFilter
一起使用,通过 AbstractAuthenticationProcessingFilter
超类中的钩子实现。它也被用于 BasicAuthenticationFilter
中。这些钩子在适当的时候调用一个具体的 RememberMeServices
。下面的列表显示了该接口。
Authentication autoLogin(HttpServletRequest request, HttpServletResponse response);
void loginFail(HttpServletRequest request, HttpServletResponse response);
void loginSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication successfulAuthentication);
关于这些方法的作用,请参见 RememberMeServices
的Javadoc,不过请注意,在这个阶段,AbstractAuthenticationProcessingFilter
只调用 loginFail()
和 loginSuccess()
方法。每当 SecurityContextHolder
不包含一个 Authentication
时, RememberMeAuthenticationFilter
就会调用 autoLogin()
方法。因此,这个接口为底层的 remember-me 实现提供了足够的认证相关事件的通知,并且每当候选web请求可能包含cookie并希望被记住时,都会委托给该实现。这种设计允许任何数量的 remember-me 实现策略。
我们在前面看到,Spring Security提供了两种实现方式。我们依次看一下这两个实现。
TokenBasedRememberMeServices
这个实现支持基于哈希简单令牌法中描述的更简单的方法。TokenBasedRememberMeServices
生成一个 RememberMeAuthenticationToken
,由 RememberMeAuthenticationProvider
处理。这个认证提供者和 TokenBasedRememberMeServices
之间共享一个密钥。此外,TokenBasedRememberMeServices
需要一个 UserDetailsService
,它可以从中获取用户名和密码用于签名比较,并生成 RememberMeAuthenticationToken
以包含正确的 GrantedAuthority
实例。 TokenBasedRememberMeServices
还实现了 Spring Security 的 LogoutHandler
接口,这样它就可以和 LogoutFilter
一起使用,让cookie自动清除。
默认情况下,该实现使用SHA-256算法对令牌签名进行编码。为了验证令牌签名,从 algorithmName
中检索的算法被解析并使用。如果没有 algorithmName
,将使用默认的匹配算法,即 SHA-256。你可以为签名编码和签名匹配指定不同的算法,这允许用户安全地升级到不同的编码算法,同时在没有 algorithmName
存在的情况下仍然能够验证旧的算法。要做到这一点,你可以把你定制的 TokenBasedRememberMeServices
指定为一个Bean,并在配置中使用它。
-
Java
-
XML
@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>
在应用 application context 中需要以下 Bean 来启用 remember-me 服务。
<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>
记住将你的 RememberMeServices
实现添加到你的 UsernamePasswordAuthenticationFilter.setRememberMeServices()
属性中,将 RememberMeAuthenticationProvider
包含在你的 AuthenticationManager.setProviders()
列表中,并将 RememberMeAuthenticationFilter
添加到你的 FilterChainProxy
中(通常紧接着你的 UsernamePasswordAuthenticationFilter
)。
PersistentTokenBasedRememberMeServices
你可以以与 TokenBasedRememberMeServices
相同的方式使用这个类,但它还需要配置一个 PersistentTokenRepository
来存储令牌。
-
`InMemoryTokenRepositoryImpl`这只用于测试。
-
JdbcTokenRepositoryImpl
它将令牌存储在数据库中。
请看 持久化令牌法 的数据库 schema。