欢迎光临
我们一直在努力

spring security中记住我的实现逻辑

一、Remember-Me 的整体工作流程

  • 用户登录:用户成功登录时,如果勾选了“记住我”,Spring Security 会生成一个 Remember-Me Cookie 并写入用户浏览器。
  • 后续请求:当用户再次访问应用时,即使没有 Session(Session 可能已过期),浏览器也会自动携带这个 Cookie。
  • 过滤器拦截:Spring Security 的 RememberMeAuthenticationFilter 会拦截该请求,从 Cookie 中解析出令牌信息。
  • 身份验证:过滤器根据令牌信息(可能是哈希值或系列标识符)去验证其有效性,并加载对应的用户信息(UserDetails)。
  • 创建认证对象:验证成功后,创建一个 RememberMeAuthenticationToken 对象,并将其设置到 SecurityContextHolder 中,表示用户已被“记住”并自动登录。
  • 访问资源:用户可以正常访问受保护的资源。
  • 二、实现方式一:简单哈希(Simple Hash)

    1.原理

    这种方式不需要服务器端存储。它通过以下信息生成一个哈希值作为令牌:用户名 、密码(或密码的哈希值)、过期时间、一个私有密钥(在应用中配置)。
    生成的令牌格式通常为:base64(username + ":" + expirationTime + ":" + md5Hex(username + ":" + expirationTime + ":" + password + ":" + key))。

    2.配置方式

    在 Spring Security 配置类中启用:

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
    .rememberMe(rememberMe -> rememberMe
    .key("uniqueAndSecretKey") // 设置私有密钥,必须足够复杂且保密
    .rememberMeCookieName("my-remember-me-cookie") // 自定义Cookie名称
    .rememberMeParameter("remember-me") // 自定义登录表单中的参数名
    .tokenValiditySeconds(3600 * 24 * 7) // 设置令牌有效期(秒),默认两周
    .useSecureCookie(true) // 仅在HTTPS下传输Cookie
    );
    return http.build();
    }

    3. 优缺点

    • 优点:实现简单,无需额外存储。
    • 缺点:
      • 安全性较低:令牌中包含了用户名和过期时间(虽然是Base64编码,但可轻易解码)。如果密钥泄露,攻击者可以伪造令牌。
      • 无法撤销:一旦令牌生成,在过期前一直有效。如果用户修改了密码,旧的令牌依然有效,存在安全风险。
      • 依赖密码:如果用户修改了密码,所有之前生成的记住我令牌都会失效(因为密码变了,哈希值对不上),这既是缺点也是一种安全特性。

    三、实现方式二:持久化令牌(Persistent Token)

    这种方式更安全、更灵活,是推荐的生产环境使用方式。它需要将令牌信息存储在数据库中。

    1. 核心概念

    它使用一个系列(Series) 的概念。一个系列对应一个用户的一组令牌。

    • 系列标识符(Series Identifier):一个随机生成的、长期有效的字符串,用于标识一组令牌。它被存储在 Cookie 中。
    • 令牌值(Token Value):一个随机生成的、短期有效的字符串,用于实际验证身份。它存储在数据库中,并且每次请求后都会更换(轮换),以防止重放攻击。

    数据库表结构

    需要创建一张表来存储令牌,Spring Security 提供了标准的建表语句:

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

    • sername: 用户名
    • series: 系列标识符(Cookie中存储的值)
    • token: 当前有效的令牌值
    • last_used: 最后一次使用的时间

    3. 工作流程

  • 生成令牌:用户登录并选择“记住我”时,系统生成一个随机的 series 和 token,将 (username, series, token, last_used) 存入数据库,并将series 和 token 组合成Cookie 发送给浏览器(例如:base64(series + ":" + token)。
  • 验证令牌:
    • 浏览器携带 Cookie 访问。
    • 过滤器解析出 series 和 token。
    • 根据 series 去数据库查询记录。
    • 比较数据库中的 token 和 Cookie 中的 token 是否匹配。
    • 如果匹配,验证通过,并生成一个新的随机 token 更新到数据库和 Cookie 中(令牌轮换),更新 last_used 时间。
    • 如果不匹配,说明令牌可能已被盗用,验证失败,并删除该系列的所有记录(撤销登录)。
  • 撤销登录:当用户主动登出、修改密码或管理员强制下线时,只需根据 username 或 series 删除数据库中的记录即可,所有记住我的会话立即失效。
  • 4. 配置方式

    • 添加依赖(如使用JDBC)
    • 配置数据源和 JdbcTemplate
    • 配置 Remember-Me 服务

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
    .rememberMe(rememberMe -> rememberMe
    .key("uniqueAndSecretKey") // 仍然需要一个密钥用于签名Cookie,防止篡改
    .tokenRepository(persistentTokenRepository()) // 注入持久化令牌仓库
    .tokenValiditySeconds(3600 * 24 * 7) // 令牌有效期
    .userDetailsService(userDetailsService) // 必须提供UserDetailsService
    );
    return http.build();
    }

    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
    JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
    tokenRepository.setDataSource(dataSource);
    // 如果表不存在,启动时自动创建(生产环境建议手动建表)
    tokenRepository.setCreateTableOnStartup(true);
    return tokenRepository;
    }

    5.优缺点

    • 优点:
      • 安全性高:令牌是随机生成的,不包含用户敏感信息。
      • 支持撤销:可以主动使令牌失效。
      • 防止重放攻击:通过令牌轮换机制,即使令牌被截获,攻击者也只能使用一次。
    • 缺点:需要额外的数据库存储和维护。

    四、核心组件解析

  • RememberMeAuthenticationFilter
    • 继承自 OncePerRequestFilter,在 doFilter 方法中工作。
    • 它从请求中读取 Remember-Me Cookie。
    • 调用配置的 RememberMeServices(如 PersistentTokenBasedRememberMeServices)的 autoLogin 方法进行验证。
    • 验证成功后,创建 RememberMeAuthenticationToken 并设置到 SecurityContextHolder。
  • RememberMeServices 接口
    • 定义了 Remember-Me 的核心服务方法:autoLogin(自动登录)、loginFail(登录失败处理)、loginSuccess(登录成功处理)、logout(登出处理)。
    • 主要实现类:
      • TokenBasedRememberMeServices:简单哈希方式的实现。
      • PersistentTokenBasedRememberMeServices:持久化令牌方式的实现。
  • RememberMeAuthenticationProvide
    • 一个认证提供者,负责处理 RememberMeAuthenticationToken 类型的认证。
    • 一个认证提供者,负责处理 RememberMeAuthenticationToken 类型的认证。

    五、安全注意事项

  • 密钥安全:key(私有密钥)必须足够长、随机且严格保密。如果泄露,攻击者可以伪造任意用户的记住我令牌。
  • 使用 HTTPS:务必配置 useSecureCookie(true),确保 Cookie 仅通过 HTTPS 传输,防止中间人攻击窃取 Cookie。
  • 设置 HttpOnly:配置 Cookie 为 HttpOnly,防止 XSS 攻击窃取 Cookie。
  • 合理的有效期:不要设置过长的 tokenValiditySeconds,在便利性和安全性之间取得平衡。
  • 敏感操作需重新认证:对于修改密码、更改邮箱等敏感操作,即使有 Remember-Me Cookie,也应要求用户重新输入密码。
  • 登出时清除:确保在用户主动登出时,调用 logout 方法清除 Remember-Me Cookie 和数据库记录(对于持久化方式)。
  • 总结

    特性简单哈希 (Simple Hash)持久化令牌 (Persistent Token)
    存储 无状态,无需存储 需要数据库存储令牌
    安全性 较低,依赖密钥保密 较高,令牌随机且可轮换
    撤销能力 无,无法主动撤销 有,可删除数据库记录
    实现复杂度 简单 中等,需建表和配置
    适用场景 简单应用,对安全要求不高 生产环境推荐,对安全要求高

    在实际项目中,强烈建议使用持久化令牌(Persistent Token)方式,并配合 HTTPS、合理的密钥管理和令牌有效期,以在保证用户体验的同时,最大程度地保障应用安全。

    赞(0)
    未经允许不得转载:171主机测评 » spring security中记住我的实现逻辑
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址