jwt 可以有 2 类 token, 一类叫ACCESS_TOKEN
用来请求接口, 一类叫REFRESH_TOKEN
用来刷新ACCESS_TOKEN
. ACCESS_TOKEN
时间比较短, 例如 10 分钟,REFRESH_TOKEN
比较久比如 7 天, 这样就可以保证 7 天内保持登录.
还有个参数ROTATE_REFRESH_TOKENS
, 当刷新ACCESS_TOKEN
时REFRESH_TOKEN
也刷新,
这样可以一直保持登录,除非在REFRESH_TOKEN
时间内都没刷新.
后端
1 2 3 4 5 6 7
| from datetime import timedelta
SIMPLE_JWT = { "ACCESS_TOKEN_LIFETIME": timedelta(minutes=30), "REFRESH_TOKEN_LIFETIME": timedelta(days=7), 'ROTATE_REFRESH_TOKENS': True, }
|
前端
封装 fetch 来处理发送 token, 刷新 token. 里面用了 chrome.storage
是浏览器扩展的接口, 自己用户换成 localStorage
就可以了
1 2 3 4 5 6 7 8 9 10 11
| # api.js import { baseUrl, http } from "./utils/fetchInstance.js";
async function login(data) { return http(baseUrl + "/api/token/", { method: "POST", body: data, }); }
export { login };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
| # utils/fetchInstance.js let baseUrl = "http://127.0.0.1:8000"; let refreshTokenUrl = baseUrl + "/api/token/refresh/"; let isRefreshing = false; let TOKEN_KEY = "token";
let getToken = async function () { if (isRefreshing) return;
let items = await chrome.storage.local.get(TOKEN_KEY); if (items[TOKEN_KEY]) { items = items[TOKEN_KEY]; } else { items = null; } return items; };
let setToken = function (value) { console.log("setToken:", value); chrome.storage.local.set({ token: value }); };
let clearToken = function () { chrome.storage.local.clear(); };
let refreshToken = async (authTokens) => { isRefreshing = true; try { let init = { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ refresh: authTokens.refresh }), }; let response = await fetch(refreshTokenUrl, init); let data = await response.json(); if (response.ok) { await setToken(data); } else { clearToken(); }
isRefreshing = false; return data; } catch (error) { isRefreshing = false; return null; } };
async function checkStatus(response) { if (response.ok) { return response; } else { if (response.status === 401) { let authTokens = await getToken(); if (authTokens) { await refreshToken(authTokens); } } return Promise.reject(response); } }
function parseJSON(response) { return response.json(); }
async function http(url, option = {}) { let headers = { "Content-Type": "application/json", };
option.headers = option.headers || headers; option.mode = option.mode || "cors";
let authTokens = await getToken(); if (authTokens) { option.headers["Authorization"] = `Bearer ${authTokens?.access}`; }
option.method = (option.method || "get").toLocaleLowerCase(); if ( option.method === "post" || option.method === "put" || option.method === "delete" ) { option.body = JSON.stringify(option.body); }
return fetch(url, option) .then(checkStatus) .then(parseJSON) .then((data) => data); }
export { baseUrl, http };
|