面试题:前端如何无感刷新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限制同时处理的请求数量。


原文始发于微信公众号(猿来是前端):面试题:前端如何无感刷新token

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/250438.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!