要在 JavaScript 中实现基于 Access Token 和 Refresh Token 的登录机制,可以按照以下步骤进行:
- 登录:用户首先提供凭证(例如用户名和密码),服务器会返回一个 Access Token 和一个 Refresh Token。
- 使用 Access Token:将 Access Token 存储在客户端(通常是
localStorage
或sessionStorage
)中,之后通过它进行 API 请求。 - 检测 Token 是否过期:在每次请求时,检查 Access Token 是否有效,如果过期,则使用 Refresh Token 获取新的 Access Token。
- 使用 Refresh Token 获取新的 Access Token:如果 Access Token 过期,使用 Refresh Token 向服务器请求一个新的 Access Token。如果 Refresh Token 也过期,则要求用户重新登录。
1. 登录流程(获取 Access 和 Refresh Token)
在用户登录时,向服务器发送用户名和密码,服务器会返回一个 Access Token 和一个 Refresh Token。这些 token 会存储在客户端(通常是 localStorage
或 sessionStorage
)中。
登录请求示例:
async function login(username, password) {
const response = await fetch('https://api.example.com/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username,
password,
}),
});
if (!response.ok) {
throw new Error('Login failed');
}
const data = await response.json();
// 将 access_token 和 refresh_token 存储在 localStorage 中
localStorage.setItem('access_token', data.access_token);
localStorage.setItem('refresh_token', data.refresh_token);
}
2. API 请求时使用 Access Token
在每次发起需要身份验证的 API 请求时,检查 Access Token 是否有效。如果 Access Token 过期,则使用 Refresh Token 获取新的 Access Token。
发送请求函数:
async function fetchData(url) {
let accessToken = localStorage.getItem('access_token');
// 如果 Access Token 存在并且有效,使用它发起请求
const response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
},
});
if (response.status === 401) {
// 如果 Access Token 过期,尝试使用 Refresh Token 获取新的 Access Token
const refreshToken = localStorage.getItem('refresh_token');
const newAccessToken = await refreshAccessToken(refreshToken);
// 如果新的 Access Token 获取成功,再次发起请求
if (newAccessToken) {
return fetchData(url); // 递归调用以重新发送请求
} else {
// 如果 Refresh Token 也过期,要求用户重新登录
alert('Session expired. Please login again.');
window.location.href = '/login'; // 跳转到登录页
}
}
return await response.json(); // 返回 API 数据
}
3. 使用 Refresh Token 刷新 Access Token
如果 Access Token 过期,你可以使用 Refresh Token 来获取新的 Access Token。这里,我们假设服务器支持刷新 token 的功能。
刷新 Access Token 函数:
async function refreshAccessToken(refreshToken) {
const response = await fetch('https://api.example.com/refresh-token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
refresh_token: refreshToken,
}),
});
if (!response.ok) {
return null; // 如果刷新失败,返回 null
}
const data = await response.json();
// 更新 localStorage 中的 access_token
localStorage.setItem('access_token', data.access_token);
return data.access_token;
}
4. 完整流程总结
- 初次登录:用户提交用户名和密码,获取 Access Token 和 Refresh Token,并存储在
localStorage
中。 - 发起请求:每次发起 API 请求时,使用
Access Token
进行授权。 - 检测过期:如果 Access Token 过期(HTTP 状态码 401),使用 Refresh Token 获取新的 Access Token。
- 刷新失败:如果 Refresh Token 也过期,则提示用户重新登录。
5. 处理过期情况的完整示例:
async function login(username, password) {
const response = await fetch('https://api.example.com/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
});
if (!response.ok) throw new Error('Login failed');
const data = await response.json();
localStorage.setItem('access_token', data.access_token);
localStorage.setItem('refresh_token', data.refresh_token);
}
async function fetchData(url) {
let accessToken = localStorage.getItem('access_token');
const response = await fetch(url, {
method: 'GET',
headers: { 'Authorization': `Bearer ${accessToken}` },
});
if (response.status === 401) {
const refreshToken = localStorage.getItem('refresh_token');
const newAccessToken = await refreshAccessToken(refreshToken);
if (newAccessToken) {
return fetchData(url); // 使用新的 Access Token 重新发起请求
} else {
alert('Session expired. Please login again.');
window.location.href = '/login'; // 重新登录
}
}
return await response.json();
}
async function refreshAccessToken(refreshToken) {
const response = await fetch('https://api.example.com/refresh-token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refresh_token: refreshToken }),
});
if (!response.ok) return null;
const data = await response.json();
localStorage.setItem('access_token', data.access_token);
return data.access_token;
}
6. 总结
- 首次登录时:客户端获取并存储 Access Token 和 Refresh Token。
- 请求时:客户端使用 Access Token 进行身份验证。如果 Access Token 过期,尝试使用 Refresh Token 获取新的 Access Token。
- Refresh Token 也过期:用户需要重新登录,获取新的凭证。
这种方法有效地管理了 Access Token 和 Refresh Token 的生命周期,能够避免用户频繁登录,同时确保安全性。