Node.js+React网站是否应将Refresh Token存储到数据库?
安全存储与验证Refresh Token的解决方案
核心结论
必须将Refresh Token的哈希值存储到数据库中——httpOnly Cookie只是客户端的安全存储方式,服务端需要独立留存记录来验证token的合法性、有效期和状态,这是防止伪造、滥用token的关键。
数据库存储方案选择
推荐采用独立的Refresh Token表,而非直接存入用户对象的数组中,原因如下:
方案1:存入用户对象(不推荐)
- 实现简单,直接在用户表加
refresh_tokens数组字段,存储token哈希值和过期时间 - 缺点:多设备登录时数组会膨胀,清理过期token或查询特定token的效率低,扩展性差
方案2:独立Refresh Token表(推荐)
创建一张refresh_tokens表,核心字段包括:
id:主键user_id:关联用户表的外键token_hash:Refresh Token的哈希值(禁止存明文)expires_at:过期时间戳revoked_at:可选,标记token是否被主动注销(NULL表示未注销)created_at:创建时间
这种方案的优势:
- 结构清晰,便于单独管理token生命周期(批量清理过期token、标记注销)
- 可通过
user_id和token_hash创建索引,查询验证效率更高 - 支持多设备登录场景,每个设备对应一条独立的token记录
完整安全流程
登录阶段
- 验证用户账号密码通过后,生成短有效期的Access Token(如15分钟)和长有效期的Refresh Token(如7天)
- 使用bcrypt或argon2对Refresh Token进行哈希处理,将哈希值、用户ID、过期时间存入
refresh_tokens表 - 将Refresh Token设置为
HttpOnly、Secure、SameSite=Strict的Cookie返回给客户端;Access Token返回给前端,由前端存在内存中(如React的state或全局状态管理工具)
刷新Access Token阶段
- 当Access Token过期,前端携带自动发送的httpOnly Cookie中的Refresh Token,向服务端发起刷新请求
- 服务端取出Refresh Token,对其哈希后查询
refresh_tokens表:- 若存在对应哈希值、未过期、未被注销,则生成新的Access Token返回给前端
- 可选实现滚动刷新:生成新的Refresh Token,替换数据库中的旧哈希值和过期时间,同时更新客户端的Cookie(旧Token立即失效)
- 若验证失败(token不存在、过期、已注销),返回401状态码,引导用户重新登录
注销阶段
- 前端发起注销请求,服务端从Cookie中取出Refresh Token,哈希后在
refresh_tokens表中标记为已注销(或直接删除记录) - 清除客户端的Refresh Token Cookie
关键安全注意事项
- 绝对不要存储明文Refresh Token:数据库泄露后,明文token会被直接滥用,哈希处理是必须的
- Cookie属性必须严格配置:
Secure确保仅HTTPS传输,SameSite=Strict防止CSRF攻击,HttpOnly避免XSS窃取 - 控制Refresh Token有效期:不要设置过长(建议7天以内),平衡安全性和用户体验
- 滚动刷新时立即失效旧Token:避免同一用户存在多个有效Refresh Token,降低泄露风险
- 定期清理冗余数据:通过定时任务删除
expires_at早于当前时间或已标记revoked_at的记录
内容的提问来源于stack exchange,提问作者Chief Jalal
相关产品推荐
相关产品推荐

