面试高频考题之双token机制💻

引言💭

在之前的文章Web身份认证与状态管理:Cookie、Session 与 JWT中提到了JWT,这篇文章再来介绍一下它的进阶版本,双Token机制。


一、什么是双 Token 机制?🤔

双 Token 机制是一种通过使用两种不同类型的令牌来管理用户身份认证和授权的方案。通常,这两种令牌是:

  1. Access Token(访问令牌):用于身份验证和授权,通常有效期较短,用于每次请求时的验证。
  2. Refresh Token(刷新令牌):用于获取新的 Access Token,通常有效期较长,帮助用户在 Access Token 过期后无缝继续访问应用。

二、JWT 与双 Token 机制的关系👥

JWT(JSON Web Token)是一种常用的令牌格式,广泛用于双 Token 机制中的 Access Token 实现。JWT 采用签名技术,确保数据的完整性和安全性。其结构由三部分组成:

  • Header(头部) :指定令牌的类型(JWT)和签名算法。
  • Payload(负载) :包含声明信息,如用户身份标识(sub)、过期时间(exp)等。
  • Signature(签名) :使用密钥对前两部分进行签名,用来验证数据完整性和防止篡改。

因此,在双 Token 机制中,Access Token 通常采用 JWT 格式,而 Refresh Token 则是另一个长期有效的令牌。两者结合使用,能够提供高效且安全的认证方案。


三、双 Token 的工作流程📌

双 Token 机制的工作流程如下:

  1. 用户登录 :用户通过用户名和密码登录系统,服务器验证用户身份后,生成并返回 Access TokenRefresh Token
  2. 访问受保护资源 :客户端使用 Access Token 向服务器请求资源,服务器验证 Access Token 的有效性。
  3. Access Token 过期 :当 Access Token 过期时,客户端通过 Refresh Token 请求新的 Access Token,无需重新登录。
  4. 获取新 Access Token :服务器验证 Refresh Token 的有效性后,返回新的 Access Token,客户端继续访问受保护资源。
  5. Refresh Token 过期 :如果 Refresh Token 过期,用户需要重新登录,重新获取新的 Access TokenRefresh Token

四、双 Token 机制的优势✨

1. 提高安全性

Access Token 存储在内存中可以减少暴露风险,因为内存中的数据在页面刷新时会清空。由于 Access Token 有较短的有效期,这样的存储方式非常适合短期使用。

Refresh Token 存储在带有 HttpOnly 属性的 cookie 中,则有效阻止了 JavaScript 访问该 token,增加了安全性。这种方法通常用于防止 XSS 攻击,使得即使攻击者能够执行 JavaScript 代码,也无法窃取 token。

这种方式结合了安全性和便捷性,确保了 Access Token 的短期使用和 Refresh Token 的长期存储在安全的位置,从而平衡了用户体验与安全性。

2. 无缝用户体验

使用 Refresh Token 可以避免用户在 Access Token 过期时重新登录。只要 Refresh Token 仍然有效,客户端就能自动获取新的 Access Token,提供无缝的用户体验,尤其适合需要长时间在线的应用,如社交媒体或电商平台。

3. 无状态认证

JWT 是无状态的,意味着服务器不需要存储会话信息。所有身份和权限信息都封装在 JWT 中,通过验证签名和有效期来确保请求的合法性。双 Token 机制进一步增强了这一点,服务器只需存储 Refresh Token,而无需管理用户的会话状态。


五、如何实现双 Token 机制?🤔

1. 生成 JWT Access Token 和 Refresh Token

使用 JWT 库生成 Access TokenRefresh Token

  • Access Token 通常包含用户的身份信息和过期时间,签名用于验证其完整性。
  • Refresh Token 长期有效,用于刷新 Access Token

代码示例:

perl 复制代码
const jwt = require('jsonwebtoken');

// 生成 Access Token
function generateAccessToken(user) {
  return jwt.sign(
  { sub: user.id }, 
  'secret', 
  { expiresIn: '15m' } // 短时效
  );
}

// 生成 Refresh Token
function generateRefreshToken(user) {
  return jwt.sign(
  { sub: user.id },
  'refreshSecret', 
  { expiresIn: '7d' } // 长时效
  );
}

2. 存储 Refresh Token

Refresh Token 可以存储在服务器端的数据库中,或通过安全的方式(如 HTTPOnly cookies)存储在客户端。它的有效期通常较长。

3. 验证 Access Token

每次客户端发起请求时,Access Token 会包含在 HTTP 头部的 Authorization 字段中,服务器通过验证其有效性来判断请求是否合法。

scss 复制代码
// 验证 Access Token
function verifyAccessToken(req, res, next) {
  // 获取 Bearer Token 的第二部分(实际 Token 值)
  const token = req.headers['authorization']?.split(' ')[1]; 
  if (!token) return res.status(403).send('Access denied.'); // 验证token是否存在
  
  // 验证token
  jwt.verify(token, 'secret', (err, user) => {
    if (err) return res.status(403).send('Invalid or expired token.');
    req.user = user; // 将解码后的用户信息(payload)附加到 `req.user` 上
    next();
  });
}

4. 使用 Refresh Token 刷新 Access Token

Access Token 过期时,客户端将 Refresh Token 发送到服务器,服务器验证 Refresh Token 后生成新的 Access Token

scss 复制代码
// 使用 Refresh Token 刷新 Access Token
function refreshAccessToken(req, res) {
  // 从请求体(body)中获取客户端提交的 refreshToken
  const refreshToken = req.body.refreshToken;
  // 检查 Refresh Token 是否存在
  if (!refreshToken) return res.status(403).send('Refresh Token required.');
  
  // 验证 Refresh Token
  jwt.verify(refreshToken, 'refreshSecret', (err, user) => {
    if (err) return res.status(403).send('Invalid refresh token.');

    // 调用 generateAccessToken 函数生成新的 Access Token 并返回给客户端
    const newAccessToken = generateAccessToken(user);
    res.json({ accessToken: newAccessToken });
  });
}

结语✒️

双 Token 机制结合了 Access TokenRefresh Token 的优势,既保证了安全性,又提升了用户体验。它使得 Web 应用能够在保证无状态认证的前提下,有效管理会话和身份验证的生命周期。

相关推荐
一直在努力的小宁1 小时前
《代码随想录-精华内容提取》07 二叉树
数据结构·算法·链表·面试
世洋Blog2 小时前
Unity面经-List底层原理、如何基于数组、如何扩容、List存储泛型、List有关在内存中的结构
unity·面试·c#·list
吃饺子不吃馅3 小时前
面试过别人后,我对面试祛魅了
前端·面试·github
uhakadotcom3 小时前
fastapi的最新版本,提供了哪些新api可供使用
前端·面试·github
小高0074 小时前
别再滥用 Base64 了——Blob 才是前端减负的正确姿势
前端·javascript·面试
进击的野人4 小时前
JavaScript原型与原型链:深入理解面向对象编程的基石
前端·javascript·面试
程序员爱钓鱼4 小时前
Python编程实战:综合项目 —— Flask 迷你博客
后端·python·面试
程序员爱钓鱼5 小时前
Python编程实战:综合项目 —— 迷你爬虫项目
后端·python·面试
Dream it possible!5 小时前
LeetCode 面试经典 150_二叉树_二叉树中的最大路径和(77_124_C++_困难)(DFS)
c++·leetcode·面试·二叉树
xiaoxue..5 小时前
深入理解JavaScript中的深拷贝与浅拷贝:内存管理的艺术
开发语言·前端·javascript·面试