面试高频考题之双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 Token 和 Refresh 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 Token 和 Refresh 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 Token 和 Refresh 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 Token 和 Refresh Token 的优势,既保证了安全性,又提升了用户体验。它使得 Web 应用能够在保证无状态认证的前提下,有效管理会话和身份验证的生命周期。

相关推荐
Yuzhiyuxia8 小时前
【AI Agent面试系列】AI Agent实战设计
人工智能·面试·职场和发展
时间的拾荒人9 小时前
Redis 哨兵(Sentinel)详解:原理、选举机制与实战部署
redis·面试·架构·sentinel
nice_lcj5209 小时前
MySQL 存储引擎入门:InnoDB 和 MyISAM 到底怎么选
java·数据库·mysql·面试·innodb·存储引擎·myisam
时间的拾荒人1 天前
Redis 主从复制详解:配置实操 + 原理机制
数据库·redis·面试
nice_lcj5201 天前
LRU 缓存入门:它是什么、怎么用、面试怎么手写
java·数据结构·缓存·面试·lru
聪明的笨猪猪2 天前
企业知识库AI架构选型:落地导向的架构决策与组件清单
经验分享·面试·架构·embedding·数据库架构
stormzhangV3 天前
涨薪 50% 怎么做到的?
人工智能·面试·aigc
聪明的笨猪猪3 天前
Java 工程案例:AI 生成 @Async 异步线程池踩坑
java·经验分享·面试·ai编程
李剑一3 天前
别再抬杠,前端岗已经没了!无论前后端,我们最终都要成“全干”工程师
前端·面试