前端面试:前端如何无感刷新token;token还没有刷新完成时,发送了其他请求需要如何处理

在前端开发中,实现无感刷新 token 通常是为了保持用户的登录状态,并确保其访问权限的有效性。下面将介绍如何实现无感刷新 token 的原理以及给出一个示例。

前端如何无感刷新token 实现原理

无感刷新 token 的实现通常基于以下几个步骤:

  1. Token 有效期:首先,服务器会为每个 token 设置一个有效期,例如 30 分钟或 1 小时。在这个有效期内,用户可以使用 token 访问受保护的资源。
  2. 定期检查:前端应用会在用户活动期间定期检查 token 的有效期。通常通过轮询或心跳机制实现,即在一定时间间隔后发送请求到服务器,检查 token 是否仍然有效。
  3. 刷新 Token :如果服务器返回 token 已失效的信息,前端应用会立即发起一个新的请求到认证服务器,使用refreshToken这个token(后端一般会返回一个长token和一个短token,长短指的是过期时间,refreshToken作为长token存在localstorage中用来向后端携带这个token去请求短token,accessToken作为短token也是存在localstorage中用这个token去请求受保护的请求放到请求头headers中)来获取新的访问 token。
  4. 无缝切换:在获取到新 token 后,前端应用会立即更新本地存储的 token,并使用新 token 继续之前的操作,从而实现了无缝的用户体验。

示例

下面是一个简单的示例,展示了如何在前端实现无感刷新 token:

  1. 设置 Token 有效期:假设服务器为每个 token 设置了 30 分钟的有效期。
  2. 定期检查:前端应用每 5 分钟向服务器发送一个请求,检查当前 token 是否仍然有效。
js 复制代码
	// 使用 setInterval 定时发送心跳请求  

	setInterval(() => {  

	  checkTokenValidity();  

	}, 5 * 60 * 1000); // 5 分钟
  1. 检查 Token 有效性:前端应用发送心跳请求到服务器,服务器返回 token 是否有效的信息。
js 复制代码
	async function checkTokenValidity() {  

	  try {  

	    const response = await fetch('/api/heartbeat', {  

	      headers: {  

	        Authorization: `Bearer ${localStorage.getItem('token')}`  

	      }  

	    });  

	  

	    if (!response.ok) {  

	      // Token 失效,需要刷新  

	      refreshToken();  

	    }  

	  } catch (error) {  

	    // 处理错误  

	    console.error('Error checking token validity:', error);  

	  }  

	}
  1. 刷新 Token:如果 token 失效,前端应用会发送一个请求到认证服务器,获取新的访问 token。
js 复制代码
	async function refreshToken() {  

	  try {  

	    const response = await fetch('/api/auth/refresh', {  

	      method: 'POST',  

	      headers: {  

	        'Content-Type': 'application/json'  

	      },  

	      body: JSON.stringify({  

	        refreshToken: localStorage.getItem('refreshToken')  

	      })  

	    });  

	  

	    if (response.ok) {  

	      const data = await response.json();  

	      // 更新本地存储的 token  

	      localStorage.setItem('accessToken', data.accessToken);  

	    } else {  

	      // 处理刷新 token 失败的情况,例如提示用户重新登录  

	      console.error('Failed to refresh token:', response.status);  

	    }  

	  } catch (error) {  

	    // 处理错误  

	    console.error('Error refreshing token:', error);  

	  }  

	}

在这个示例中,前端应用通过定期检查 token 的有效性,并在 token 失效时无缝刷新 token,从而保持了用户的登录状态,并提供了无缝的用户体验。需要注意的是,为了安全起见,刷新 token 也应该有一定的有效期,并在过期后要求用户重新登录。

token还没有刷新完成时,发送了其他请求需要如何处理

如果刷新的 token 还没有返回,但是你已经使用旧的 token 发送了其他三个请求到服务端,那么可能会遇到以下几种情况:

请求被拒绝:如果服务端检查到 token 已经过期,它可能会返回一个 401 Unauthorized 或者类似的错误状态码。在这种情况下,你需要捕获这些错误,并在捕获到错误后尝试使用新的 token 重新发送这些请求。

为了处理这些情况,你可以采取以下策略:

  • 错误处理:确保你的应用能够捕获和处理 401 Unauthorized 或其他相关错误。当捕获到这些错误时,你可以尝试使用新的 token 重新发送请求。
  • 请求重试机制:实现一个请求重试机制,当请求失败时,可以自动或手动触发重试,并使用新的 token。你可以使用指数退避策略来避免频繁重试对服务端造成压力。
  • 状态管理:在 token 刷新期间,你可以将应用的状态设置为"正在刷新 token",并禁止发送新的请求,直到获取到新的 token。这样可以避免使用无效的 token 发送更多请求。
  • 队列请求:在 token 过期后,而不是立即发送请求,你可以将请求放入队列中,等待新的 token 获取后再依次发送。这可以通过使用 Promise.all 或者其他异步处理技术来实现。
  • 前端提示 :在 token 过期后,你可以在前端显示一个提示,告知用户正在刷新 token,并可能需要等待一段时间或者重新登录。
    下面是一个简化的示例代码,

展示了如何在捕获到 401 错误后使用新 token 重新发送请求:

js 复制代码
	// 假设这是你的 API 请求函数  

	async function apiRequest(url, token) {  

	  try {  

	    const response = await fetch(url, {  

	      headers: {  

	        Authorization: `Bearer ${token}`  

	      }  

	    });  

	  

	    if (!response.ok) {  

	      throw new Error(`Request failed with status ${response.status}`);  

	    }  

	  

	    return response.json();  

	  } catch (error) {  

	    if (error.message.includes('401') && newToken) {  

	      // 尝试使用新 token 重新发送请求  

	      return apiRequest(url, newToken);  

	    }  

	    throw error;  

	  }  

	}  

	  

	// 假设这是你的 token 刷新函数  

	let newToken = null; // 存储新 token 的变量  

	  

	async function refreshToken() {  

	  try {  

	    const response = await fetch('/api/auth/refresh', {  

	      // ... 发送刷新请求的代码 ...  

	    });  

	  

	    if (response.ok) {  

	      const data = await response.json();  

	      newToken = data.token;  

	    }  

	  } catch (error) {  

	    // 处理错误  

	  }  

	}  

	  

	// 当 token 过期时调用此函数  

	async function handleTokenExpiration() {  

	  try {  

	    // 尝试刷新 token  

	    await refreshToken();  

	  

	    // 假设这是之前因为 token 过期而失败的请求数组  

	    const failedRequests = [/* ... */];  

	  

	    // 使用新 token 重新发送请求  

	    for (const request of failedRequests) {  

	      try {  

	        const response = await apiRequest(request.url, newToken);  

	        // 处理响应或更新应用状态  

	      } catch (error) {  

	        // 处理重新发送请求时的错误  

	      }  

	    }  

	  } catch (error) {  

	    // 处理刷新 token 或重新发送请求时的错误  

	  }  

	}

在这个示例中,apiRequest 函数会在遇到 401 错误时检查是否有新的 token 可用,并尝试使用新 token 重新发送请求。handleTokenExpiration 函数则负责在 token 过期时刷新 token,并重新发送之前失败的请求。

队列请求

队列请求的实现主要是利用队列这种数据结构来管理待处理的请求。当某个请求因为某些原因(比如token过期)需要延迟处理时,我们可以将这个请求放入队列中,等待条件满足(比如获取到新的token)后再从队列中取出请求进行处理。

下面是一个简单的例子来说明队列请求的实现:

1. 创建请求队列

首先,我们需要一个队列来存储待处理的请求。这个队列可以是内存中的一个数组、链表,或者使用一些现成的队列库(如JavaScript中的Array.prototype.queuequeue-promise库)。

js 复制代码
	let requestQueue = []; // 使用数组作为简单的队列实现

2. 入队操作

当需要延迟处理一个请求时,我们将这个请求添加到队列的末尾。

js 复制代码
	function enqueueRequest(request) {  

	  requestQueue.push(request);  

	}

3. 出队操作

当条件满足(比如获取到新的token)时,我们从队列的头部取出一个请求进行处理,并重复这个过程直到队列为空。

js 复制代码
	async function dequeueAndProcessRequests() {  

	  while (requestQueue.length > 0) {  

	    const request = requestQueue.shift(); // 从队列头部取出一个请求  

	    try {  

	      const response = await processRequest(request); // 处理请求  

	      // 处理响应或更新应用状态  

	    } catch (error) {  

	      // 处理请求处理过程中的错误  

	      console.error('Error processing request:', error);  

	    }  

	  }  

	}

4. 处理请求

processRequest函数负责实际处理请求的逻辑。在这个函数中,你可以发送HTTP请求、更新UI等。

js 复制代码
	async function processRequest(request) {  

	  // 这里是处理请求的逻辑,比如发送HTTP请求  

	  const response = await fetch(request.url, {  

	    headers: {  

	      Authorization: `Bearer ${newToken}` // 使用新的token  

	    }  

	  });  

	  return response.json(); // 返回处理结果  

	}

5. 使用示例

当token过期时,我们可以将需要延迟处理的请求加入队列,然后尝试刷新token。一旦获取到新的token,我们从队列中取出请求并处理它们。

js 复制代码
	// 假设这是因为token过期而需要延迟处理的请求  

	const delayedRequest = { url: '/api/data' };  

	  

	// 将请求加入队列  

	enqueueRequest(delayedRequest);  

	  

	// 尝试刷新token  

	await refreshToken();  

	  

	// 处理队列中的请求  

	await dequeueAndProcessRequests();

注意事项

  • 队列大小管理:如果请求队列变得非常大,可能会消耗大量内存。你可能需要实现一些策略来限制队列的大小,比如设置最大长度、淘汰最旧的请求等。
  • 错误处理:在处理请求和响应时,务必进行适当的错误处理,以避免应用崩溃或产生不可预期的行为。
  • 并发控制:如果你的应用需要处理大量并发请求,你可能需要实现一些机制来控制并发数,比如使用Promise.all限制同时处理的请求数量。
相关推荐
前端之虎陈随易2 小时前
编程语言级别的Skill市场,AI Agent 的未来形态
前端·vue.js·人工智能·typescript·node.js
一路向北he2 小时前
字节钢铁军团--“提供情境,而非控制”
java·开发语言·前端
kyriewen3 小时前
豆包和千问同时关了智能体,我用它们搭的 3 个自动化全废了——迁移方案整理
前端·javascript·ai编程
前端一小卒3 小时前
我用 TypeScript 从零手写了一个 Claude Code,然后发现它的核心只有 30 行
前端·agent
Waay3 小时前
面试口述版:个人对 Prometheus 完整理解
运维·学习·云原生·面试·职场和发展·kubernetes·prometheus
大圣编程5 小时前
Python中continue语句的用法是什么?
开发语言·前端·python
yuhaiqiang5 小时前
随手 vibecoding 的浏览器插件已经 6000 多次下载,聊聊他的产品设计
前端·后端·面试
之歆5 小时前
Vue商品详情与放大镜组件
前端·javascript·vue.js
再吃一根胡萝卜6 小时前
如何把小米 MiMo 接入 CodeBuddy,打造私有 Agent
前端
负责的蛋挞7 小时前
异步HttpModule的实现方式
java·服务器·前端