從此看世界

fetch API 前端開發 refreshToken 無感刷新訪問 Access Token

🕗2024-10-31

在前端開發中,管理 Token 經常讓人頭疼,尤其是想要實現無感刷新。本文會介紹幾種方式,和一個用 Fetch 完成的範例,讓你更容易理解這些方法。一起來探討吧!

▍三種無感刷新 Token 的解法

1. 利用後端提供的過期時間

  • 優點: 簡單直接,只需讀取後端提供的過期時間字段來決定是否刷新Token。
  • 缺點: 若本地時間被篡改或與伺服器時間不同步,則可能導致誤判。
if (Date.now() > tokenExpiryTime) {
// 刷新 Token
fetch('/refresh-token', { method: 'POST' });
}

2. 使用定時器進行定期刷新

  • 優點: 保證 Token 總是有效的,不需額外的判斷邏輯。
  • 缺點: 即使使用者未進行活動,Token 仍然會進行更新,導致大量的資源使用。
setInterval(() => {
// 定期刷新Token
fetch('/refresh-token', { method: 'POST' });
}, tokenRefreshInterval);

3. 響應攔截器中進行刷新

  • 優點: 只有在必要的時候才刷新 Token,資源利用更高效。
  • 缺點: 需要較複雜的前端邏輯,特別是在處理多個並行請求的情況。
fetch('/some-api-endpoint')
.then(res => {
if (res.status === 401) {
// 刷新 Token 並重新發送請求
return fetch('/refresh-token', { method: 'POST' });
}
return res.json();
})
.then(data => {
// 處理數據
});

▍最佳實踐:使用 React 實作響應攔截器進行Token 刷新

在前端開發中,對 Token 的管理是不容忽視的一環。經過對比不同的無感刷新 Token 的策略後,第三種方法:在響應攔截器中進行刷新,是我工作中最常使用,也是最推薦的一種方式。

為什麼第三種最好?

這種方法的優點是,Token 只會在實際需要呼叫 API 時才會被檢查和更新,達到最高的經濟效用和效率。這就像你只會在車油快用完時,才會去加油站加油,而不是每開一段距離就去加一次。這樣不僅節省了時間,也提高了整體的性能和使用者體驗。

使用 React 實作第三種方法

接下來,我們將利用 React 來具體實現這個方法。這會包括使用 useEffect來執行副作用操作,利用原生fetch來進行 API 呼叫,當 API 回傳 401 狀態碼,代表 Token 需要刷新,我們就會在「響應攔截器」中處理來實現 Token 的無感刷新。

401 狀態碼是 HTTP 協議中定義的一個代表「未授權」(Unauthorized)的狀態碼。在許多API認證流程中,當 Token 無效或過期時,後端通常會返回這個狀態碼。這是一個標準的做法,用於告知前端:目前的請求由於缺乏有效的身份驗證憑證而被拒絕。

這種方法的好處是非常直觀和容易實施,我們可以確保只有在真正需要 Token 並且 Token 過期的情況下才進行刷新,這樣不僅效率更高,也能提供更好的用戶體驗。

import { useEffect, useState } from "react";
import Cookies from "js-cookie";
import Loading from "@components/Loading";

function App() {
const [data, setData] = useState(null);

useEffect(() => {
// 響應攔截器
async function fetchData() {
const res = await fetch("/api/some-endpoint", {
headers: {
Authorization: `Bearer ${Cookies.get('token')}`
}
});

if (res.status === 401) {
// Token 過期,用 Refresh Token 換取新 Token
const refreshRes = await fetch("/api/refresh-token", { method: "POST" });
const newTokenData = await refreshRes.json();
const newToken = newTokenData.token;
const expiresIn = newTokenData.expires_in;

// 儲存新 Token 到 cookie 並設置過期時間,然後重新發送原請求
Cookies.set('token', newToken, { expires: expiresIn / 86400 });
return fetchData();
} else {
const result = await res.json();
setData(result);
}
}

fetchData();
}, []);

return (
<div>
{data ? <pre>{JSON.stringify(data, null, 2)}</pre> : <Loading />}
</div>
);
}

export default App;

透過範例,可以確保在整個應用的生命週期中,Token 都能在需要的時候得到及時而有效的管理和更新。這不僅能提升應用的安全性,還能改善使用者的體驗。