微信登录

记住我功能 - 令牌存储 - 存储记住我令牌

Java - Web - Spring 《记住我功能 - 令牌存储 - 存储记住我令牌》

在 Web 应用程序中,“记住我”功能是一项非常实用的功能,它允许用户在下次访问网站时无需再次输入用户名和密码。Spring Security 提供了对“记住我”功能的支持,并且可以通过不同的方式来存储记住我的令牌。本文将详细介绍如何在 Spring 应用中实现“记住我”功能,并使用不同的令牌存储方式。

1. 记住我功能概述

“记住我”功能的核心原理是在用户登录时生成一个令牌(通常是一个加密的字符串),并将其存储在客户端(通常是 Cookie)和服务器端。当用户下次访问网站时,客户端会将该令牌发送给服务器,服务器验证该令牌的有效性,如果有效则自动为用户进行登录。

2. Spring Security 中的记住我功能

Spring Security 提供了 RememberMeServices 接口来处理记住我功能。默认情况下,Spring Security 提供了两种令牌存储方式:

  • InMemoryTokenRepositoryImpl:将令牌存储在内存中,适用于开发和测试环境,但不适合生产环境,因为服务器重启后令牌会丢失。
  • PersistentTokenRepository:将令牌持久化存储在数据库中,适用于生产环境。

2.1 配置 Spring Security

首先,我们需要在 Spring Security 配置中启用记住我功能。以下是一个简单的配置示例:

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  4. import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
  5. import org.springframework.security.web.SecurityFilterChain;
  6. @Configuration
  7. @EnableWebSecurity
  8. public class SecurityConfig {
  9. @Bean
  10. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  11. http
  12. .authorizeRequests()
  13. .anyRequest().authenticated()
  14. .and()
  15. .formLogin()
  16. .and()
  17. .rememberMe()
  18. .key("uniqueAndSecret")
  19. .tokenValiditySeconds(86400); // 令牌有效期为 24 小时
  20. return http.build();
  21. }
  22. }

在上述配置中,我们使用 rememberMe() 方法启用了记住我功能,并设置了一个唯一的密钥 key 和令牌的有效期 tokenValiditySeconds

2.2 使用 InMemoryTokenRepositoryImpl

InMemoryTokenRepositoryImpl 是 Spring Security 提供的一个简单的令牌存储实现,它将令牌存储在内存中。以下是如何使用它的示例:

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  4. import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
  5. import org.springframework.security.web.SecurityFilterChain;
  6. import org.springframework.security.web.authentication.rememberme.InMemoryTokenRepositoryImpl;
  7. @Configuration
  8. @EnableWebSecurity
  9. public class SecurityConfig {
  10. @Bean
  11. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  12. InMemoryTokenRepositoryImpl tokenRepository = new InMemoryTokenRepositoryImpl();
  13. http
  14. .authorizeRequests()
  15. .anyRequest().authenticated()
  16. .and()
  17. .formLogin()
  18. .and()
  19. .rememberMe()
  20. .key("uniqueAndSecret")
  21. .tokenValiditySeconds(86400)
  22. .tokenRepository(tokenRepository);
  23. return http.build();
  24. }
  25. }

2.3 使用 PersistentTokenRepository

PersistentTokenRepository 是一个接口,Spring Security 提供了一个默认的实现 JdbcTokenRepositoryImpl,它将令牌存储在数据库中。以下是如何使用它的示例:

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  4. import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
  5. import org.springframework.security.web.SecurityFilterChain;
  6. import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
  7. import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
  8. import javax.sql.DataSource;
  9. @Configuration
  10. @EnableWebSecurity
  11. public class SecurityConfig {
  12. private final DataSource dataSource;
  13. public SecurityConfig(DataSource dataSource) {
  14. this.dataSource = dataSource;
  15. }
  16. @Bean
  17. public PersistentTokenRepository persistentTokenRepository() {
  18. JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
  19. tokenRepository.setDataSource(dataSource);
  20. // 如果数据库中没有相应的表,可以使用以下方法自动创建
  21. // tokenRepository.setCreateTableOnStartup(true);
  22. return tokenRepository;
  23. }
  24. @Bean
  25. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  26. http
  27. .authorizeRequests()
  28. .anyRequest().authenticated()
  29. .and()
  30. .formLogin()
  31. .and()
  32. .rememberMe()
  33. .key("uniqueAndSecret")
  34. .tokenValiditySeconds(86400)
  35. .tokenRepository(persistentTokenRepository());
  36. return http.build();
  37. }
  38. }

数据库表结构

JdbcTokenRepositoryImpl 需要一个特定的数据库表来存储令牌。以下是创建该表的 SQL 语句:

  1. CREATE TABLE persistent_logins (
  2. username VARCHAR(64) NOT NULL,
  3. series VARCHAR(64) PRIMARY KEY,
  4. token VARCHAR(64) NOT NULL,
  5. last_used TIMESTAMP NOT NULL
  6. );

3. 总结

令牌存储方式 适用场景 优点 缺点
InMemoryTokenRepositoryImpl 开发和测试环境 简单易用,无需数据库 服务器重启后令牌丢失,不适合生产环境
JdbcTokenRepositoryImpl 生产环境 令牌持久化存储,服务器重启后令牌不会丢失 需要配置数据库,相对复杂

通过上述示例,我们可以看到在 Spring 应用中实现“记住我”功能并存储令牌是非常简单的。根据不同的应用场景,我们可以选择合适的令牌存储方式。

记住我功能 - 令牌存储 - 存储记住我令牌