李锋镝的博客

  • 首页
  • 时间轴
  • 评论区显眼包🔥
  • 左邻右舍
  • 博友圈
  • 关于我
    • 关于我
    • 另一个网站
    • 我的导航站
    • 网站地图
    • 赞助
  • 留言
  • 🚇开往
Destiny
自是人生长恨水长东
  1. 首页
  2. 原创
  3. 正文

双 Token 机制

2025年5月19日 514点热度 0人点赞 4条评论

一、什么是双 Token 机制?

双Token机制是通过两种令牌管理用户认证与授权的方案,核心令牌包括:

  • Access Token(访问令牌)
    • 用于身份验证和授权,每次请求时携带。
    • 有效期短(如15分钟),降低泄漏风险。
  • Refresh Token(刷新令牌)
    • 用于在Access Token过期后获取新令牌。
    • 有效期长(如7天),支持无缝续期。

二、JWT 与双 Token 机制的关系

1. JWT的结构

JWT是双Token机制中Access Token的常用格式,由三部分组成:

  • Header(头部):指定令牌类型(JWT)和签名算法(如HMAC SHA256)。
  • Payload(负载):包含用户标识(sub)、过期时间(exp)、权限等声明。
  • Signature(签名):使用密钥对头部和负载签名,验证数据完整性。

2. 双Token的分工

  • Access Token:采用JWT格式,携带用户认证信息。
  • Refresh Token:独立令牌,用于刷新Access Token,通常存储于安全位置。

三、双 Token 的工作流程

  1. 用户登录

    • 用户提交用户名/密码,服务器验证通过后返回Access Token(短时效)和Refresh Token(长时效)。
  2. 正常访问流程

    • 客户端在请求头中携带Access Token(如Authorization: Bearer <token>)。
    • 服务器验证Access Token有效后,返回请求资源。
  3. Access Token过期

    • 客户端检测到Access Token过期,向服务器发送Refresh Token。
    • 服务器验证Refresh Token有效,生成新的Access Token并返回。
  4. Refresh Token过期

    • 若Refresh Token失效,用户需重新登录以获取新的双Token。

四、双 Token 机制的优势

1. 提高安全性

  • Access Token存储:存于内存(如JavaScript变量),页面刷新即清空,减少持久化泄漏风险。
  • Refresh Token存储:通过HttpOnly Cookie存储,防止XSS攻击窃取令牌。

2. 无缝用户体验

  • 自动刷新Access Token,避免频繁登录,适合长会话场景(如电商、社交应用)。

3. 无状态认证

  • JWT自身携带用户信息,服务器无需存储会话状态,仅需验证Refresh Token(可选存储于数据库)。

五、如何实现双 Token 机制?

1. 生成令牌(代码示例)

const jwt = require('jsonwebtoken');

// 生成Access Token(15分钟过期)
function generateAccessToken(user) {
  return jwt.sign(
    { sub: user.id, role: user.role }, // 负载信息
    'access_secret_key', // 签名密钥
    { expiresIn: '15m' }
  );
}

// 生成Refresh Token(7天过期)
function generateRefreshToken(user) {
  return jwt.sign(
    { sub: user.id }, // 简化负载(仅需用户标识)
    'refresh_secret_key',
    { expiresIn: '7d' }
  );
}

2. 存储Refresh Token

  • 服务器端:存入数据库,关联用户ID(适合高安全场景)。
  • 客户端:通过HttpOnly Cookie存储(需配合Secure属性,避免明文传输)。

3. 验证Access Token

function verifyAccessToken(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1]; // 解析Bearer Token
  if (!token) return res.status(401).json({ message: '缺少访问令牌' });

  jwt.verify(token, 'access_secret_key', (err, decoded) => {
    if (err) return res.status(403).json({ message: '令牌无效或过期' });
    req.user = decoded; // 将用户信息附加到请求对象
    next(); // 继续处理请求
  });
}

4. 刷新Access Token

function refreshTokenHandler(req, res) {
  const refreshToken = req.body.refreshToken; // 从请求体获取Refresh Token

  // 验证Refresh Token
  jwt.verify(refreshToken, 'refresh_secret_key', (err, user) => {
    if (err) return res.status(403).json({ message: '刷新令牌无效' });

    // 生成新的Access Token
    const newAccessToken = generateAccessToken(user);
    res.json({ accessToken: newAccessToken });
  });
}
除非注明,否则均为李锋镝的博客原创文章,转载必须以链接形式标明本文链接

本文链接:https://www.lifengdi.com/article/4403

相关文章

  • JWT 实现登录认证 + Token 自动续期方案
  • SpringBoot常用注解
  • CompletableFuture使用详解
  • 金融级JVM深度调优实战的经验和技巧
  • SpringBoot 中内置的 49 个常用工具类
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
标签: JAVA JWT SSO Token 登录
最后更新:2025年5月19日

李锋镝

既然选择了远方,便只顾风雨兼程。

打赏 点赞
< 上一篇
下一篇 >

文章评论

  • GoodBoyboy青铜

    然而令我难蚌的是,我们学校找人开发的应用,accessToken有效期超长(几个月),refreshToken没用,而且在post请求中将jwt放在url路径中,我都懒得喷了

    Windows
    Firefox 131.0 中国-湖南
    2025年6月23日
    回复
    • 李锋镝管理

      @GoodBoyboy 哈哈

      macOS
      Chrome 137.0.0.0 中国-北京
      2025年6月24日
      回复
    • duzhuo黑铁

      @GoodBoyboy 哈哈 兄弟那我看眼日志你不炸了吗

      Linux
      Chrome 138.0.0.0 中国-湖北-武汉
      2025年7月24日
      回复
      • GoodBoyboy青铜

        @duzhuo 世界就是一个巨大的草台班子

        Windows
        Firefox 131.0 中国-湖南
        2025年7月25日
        回复
  • 1 2 3 4 5 6 7 8 9 11 12 13 14 15 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 46 47 48 49 50 51 52 53 54 55 57 58 60 61 62 63 64 65 66 67 69 72 74 76 77 78 79 80 81 82 85 86 87 90 92 93 94 95 96 97 98 99
    取消回复

    东风夜放花千树。更吹落、星如雨。宝马雕车香满路。凤箫声动,玉壶光转,一夜鱼龙舞。
    蛾儿雪柳黄金缕。笑语盈盈暗香去。众里寻他千百度。蓦然回首,那人却在,灯火阑珊处。

    那年今日(05月15日)

    • 1948年:以色列和阿拉伯国家之间的第一次中东战争爆发
    • 1889年:法国埃菲尔铁塔于世界博览会上正式对外开放
    • 1859年:法国物理学家皮埃尔·居里出生
    • 1773年:奥国外交家克莱门斯·梅特涅出生
    • 1567年:意大利作曲家蒙台威尔第出生
    • 更多历史事件
    最新 热点 随机
    最新 热点 随机
    SchedulingConfigurer详解 踩坑60+次后,我终于搞懂 Claude Skill 怎么写才会真的触发 Everything Claude Code 详细使用文档 配置Jackson使用字段而不是getter/setter来序列化和反序列化 这个域名注册整整十年了,十年时间,真快啊 Claude Code全维度实战指南:从入门到精通,解锁AI编程新范式
    AI时代,个人技术博客的出路在哪里?这个域名注册整整十年了,十年时间,真快啊WordPress实现用户评论等级排行榜插件WordPress网站换了个字体,差点儿把样式换崩了做了一个WordPress文章热力图插件千万级大表新增字段实战指南:告别锁表与业务中断
    TIOBE 12月榜单:C#有望摘得年度语言,R语言重返Top 10 看病难~取药难~~ 醒醒~补个税了 从SQL规范性检查、表结构索引检查着手分析如何优化SQL 企业级自动化 Agent 架构深析:Prompt 演进驱动的智能工作流落地 redis异常记录
    标签聚合
    日常 分布式 JAVA 架构 数据库 SQL MySQL JVM WordPress AI IDEA AI编程 docker K8s Spring 多线程 Redis SpringBoot 设计模式 ElasticSearch
    友情链接
    • Blogs·CN
    • Honesty
    • Mr.Sun的博客
    • 临窗旋墨
    • 哥斯拉
    • 彬红茶日记
    • 志文工作室
    • 懋和道人
    • 拾趣博客导航
    • 搬砖日记
    • 旧时繁华
    • 林羽凡
    • 瓦匠个人小站
    • 皮皮社
    • 知向前端
    • 蜗牛工作室
    • 韩小韩博客
    • 风渡言

    COPYRIGHT © 2026 lifengdi.com. ALL RIGHTS RESERVED.

    域名年龄

    Theme Kratos Made By Dylan

    津ICP备2024022503号-3

    京公网安备11011502039375号