JavaScript 中实现基于 Access Token 和 Refresh Token 的登录机制

要在 JavaScript 中实现基于 Access TokenRefresh Token 的登录机制,可以按照以下步骤进行:

  1. 登录:用户首先提供凭证(例如用户名和密码),服务器会返回一个 Access Token 和一个 Refresh Token
  2. 使用 Access Token:将 Access Token 存储在客户端(通常是 localStoragesessionStorage)中,之后通过它进行 API 请求。
  3. 检测 Token 是否过期:在每次请求时,检查 Access Token 是否有效,如果过期,则使用 Refresh Token 获取新的 Access Token
  4. 使用 Refresh Token 获取新的 Access Token:如果 Access Token 过期,使用 Refresh Token 向服务器请求一个新的 Access Token。如果 Refresh Token 也过期,则要求用户重新登录。

1. 登录流程(获取 Access 和 Refresh Token)

在用户登录时,向服务器发送用户名和密码,服务器会返回一个 Access Token 和一个 Refresh Token。这些 token 会存储在客户端(通常是 localStoragesessionStorage)中。

登录请求示例:

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 TokenRefresh 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 TokenRefresh Token
  • 请求时:客户端使用 Access Token 进行身份验证。如果 Access Token 过期,尝试使用 Refresh Token 获取新的 Access Token
  • Refresh Token 也过期:用户需要重新登录,获取新的凭证。

这种方法有效地管理了 Access TokenRefresh Token 的生命周期,能够避免用户频繁登录,同时确保安全性。

Scroll to Top