🧑💻 寫在開頭
點贊 + 收藏 === 學會🤣🤣🤣
開篇:一個經典的面試題
“説説看,用户登錄後拿到的 Token,前端應該怎麼存?”
這個問題看似簡單,卻能清晰地分辨出一個前端開發者對安全的理解深度。是存到 localStorage?sessionStorage?還是 Cookie?又或者是內存裏?不同的選擇背後,是截然不同的安全考量。
今天,來聊一聊 Token 的存儲之道,讓你不僅知道怎麼做,更明白為什麼這麼做。
選項一:Web Storage(localStorage / sessionStorage)
這是最直觀、最容易想到的方案。
// 登錄成功後
const token = 'your_jwt_token_here';
localStorage.setItem('auth_token', token);
// 後續請求時帶上
axios.interceptors.request.use((config) => {
const token = localStorage.getItem('auth_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
優點:
- 簡單易用:API 簡單,上手快。
- 永久存儲(
localStorage):除非手動清除,否則一直存在。 - 會話期存儲(
sessionStorage):頁面關閉即清除,更安全一點。
致命缺點:
- 極易受到 XSS (跨站腳本攻擊) 的攻擊。這是最核心的問題。如果網站存在 XSS 漏洞,攻擊者的惡意腳本可以輕易地讀取到
localStorage中的所有 Token,從而竊取用户身份。
結論: 不推薦,尤其對於敏感應用。 除非你的應用完全不存在 XSS 風險(但這幾乎不可能),或者 Token 的安全級別要求不高。
選項二:Cookie
Cookie 是傳統且常見的身份驗證載體。
// 服務端設置 Cookie (HTTP Response Header) Set-Cookie: auth_token=your_jwt_token_here; Path=/; HttpOnly; Secure // 前端無需特殊處理,瀏覽器會自動在每次請求中攜帶
注意這裏的兩個關鍵屬性:
HttpOnly:這是對抗 XSS 的神器。設置了HttpOnly的 Cookie 無法通過 JavaScript 的document.cookieAPI 訪問,這意味着即使發生 XSS,攻擊者也無法竊取到 Token。Secure:強制 Cookie 只能在 HTTPS 協議下被髮送,防止在網絡傳輸中被竊聽。
優點:
- 免疫 XSS(得益於
HttpOnly):無法通過 JS 讀取,安全性高。 - 可控制生命週期:通過
Expires或Max-Age設置過期時間。 - 自動管理:瀏覽器自動在同源請求中攜帶。
缺點:
- 容易受到 CSRF (跨站請求偽造) 的攻擊。因為瀏覽器會自動在請求中帶上 Cookie,攻擊者可以誘導用户點擊一個鏈接,從而以用户的身份發起惡意請求。
- 需要額外的 CSRF 防護措施,如使用 Anti-CSRF Token、驗證 Origin/Referer Header 等。
結論: 是一個可行的方案,但必須配套完善的 CSRF 防禦機制。
選項三:內存(Memory)
將 Token 保存在 JavaScript 變量中。
let inMemoryToken = null;
// 登錄成功後
const login = async () => {
const response = await loginAPI(username, password);
inMemoryToken = response.data.token; // 存到內存變量
};
// 請求攔截器中添加
axios.interceptors.request.use((config) => {
if (inMemoryToken) {
config.headers.Authorization = `Bearer ${inMemoryToken}`;
}
return config;
});
// 退出登錄或頁面刷新時,Token 消失
優點:
- 安全性極高:由於 Token 只存在於當前頁面的內存中,關閉頁面或刷新頁面後 Token 立即消失。XSS 攻擊者很難通過一次性注入的腳本持續地竊取到 Token(除非攻擊代碼常駐內存)。
缺點:
- 體驗差:頁面一旦刷新,Token 就沒了,用户需要重新登錄。這對於單頁面應用 (SPA) 來説是致命的。
結論: 適用於安全要求極高、不介意頻繁登錄的場景(如銀行系統)。 對於普通 Web 應用,體驗不可接受。
終極方案:組合拳 + 架構思維
既然沒有完美的銀彈,現代前端的最佳實踐通常是 組合方案 和 架構優化。
實踐一:Cookie(HttpOnly + Secure) + 防禦 CSRF
這是最傳統但依然非常穩健的方案。
- 後端在登錄成功後,設置一個
HttpOnly、Secure的 Cookie 來存放 Token(可以是 JWT,也可以是 Session ID)。 - 前端基本不用操心 Token 的存儲和攜帶問題,由瀏覽器自動完成。
- 後端必須部署完善的 CSRF 防護策略,例如:
- 從 Cookie 中讀取 Token 或 Session ID 進行身份驗證。
- 同時,要求請求必須攜帶一個額外的(由後端生成並通過 API 返回給前端的)
X-CSRF-TokenHeader,並與 Session 中存儲的值進行比對。
實踐二:Access Token + Refresh Token
這是目前 API 接口認證非常流行的方案,完美解決了內存存儲體驗差的問題。
- 登錄:用户輸入密碼登錄。
- 返回雙 Token:服務端返回兩個 Token:
access_token:短期有效(例如 2 小時),用於請求受保護的 API。refresh_token:長期有效(例如 7 天),僅用於獲取新的access_token,不應具備API訪問權限。
- 存儲策略:
access_token:存入內存。這樣即使被 XSS 竊取,有效期也很短,風險可控。refresh_token:存入HttpOnlyCookie。因為它有效期長,必須嚴加保護。由於其本身不直接用於業務請求,即使遭遇 CSRF,攻擊者也無法用它來做任何關鍵操作(只能用來換一個短期的access_token,而該access_token又會因為存在於內存而難以被攻擊者獲取)。
- 無感刷新:當
access_token過期後,前端自動(通過refresh_token)調用刷新接口獲取新的access_token,用户無感知。
這個方案在安全性和用户體驗之間取得了絕佳的平衡,是當前眾多大型應用的首選。
總結與建議
給你的最終建議:
- 如果你在做的是一個簡單的、內部使用的、對安全性要求不高的工具,用
localStorage圖個方便也無可厚非,但要清楚風險。 - 如果你在做的是一個傳統的、服務端渲染的多頁應用,使用
HttpOnlyCookie 並配套 CSRF 防護是標準做法。 - 如果你在做的是一個現代化的 SPA(如 React/Vue 應用),強烈推薦研究並採用 Access Token (內存) + Refresh Token (
HttpOnlyCookie) 的方案。
安全沒有絕對,只有相對的權衡。理解每種方案的利弊,並根據你的實際業務場景做出最適合的選擇,才是最重要的。