跳转至

零停机迁移

零停机迁移的做法与批量迁移类似,区别在于已经通过旧身份验证系统登录的用户不需要重新登录。实现方式是获取旧系统签发的访问令牌,验证该令牌,然后用它换取 Authany 签发的新访问令牌。

要用旧系统中有效的访问令牌进行兑换,需要实现一个迁移端点,调用 Authany Admin API 的 CreateSession mutation。

零停机迁移流程

何时使用零停机迁移

零停机迁移适用于以下场景:

  • 需要在迁移后保持用户登录状态,避免用户重新登录。
  • 应用中有大量已登录用户,且通常不要求他们每次访问都重新登录。

优点

用户不会被登出,因此无需重新登录。

缺点

配置和部署更为复杂。

说明

注意:出于安全考虑,CreateSession mutation 默认处于禁用状态。因此,您需要联系我们为您的项目启用该功能。

本指南将介绍如何执行零停机迁移,让当前通过旧身份验证系统登录的用户在整个迁移过程中都不会被登出,无缝迁移到 Authany。

本指南将参考一个 Express.js 演示应用的源代码。该应用会获取一个经过验证的访问令牌,并使用与该令牌关联的用户在现有身份验证提供方中的详细信息,为该用户创建一个新的 Authany 用户会话(访问令牌)。

第 1 步:禁止在旧身份验证系统中更新用户数据

首先,临时限制用户在旧系统中修改密码和其他资料信息,以避免迁移过程中出现数据不一致。

具体的操作方式因您使用的旧身份验证提供方而异,请参阅其官方文档了解详情。

第 2 步:将用户数据导入 Authany

使用用户导入 API,将您已从旧系统导出的用户数据导入 Authany。

您的用户数据必须按照用户导入 API 数据结构重新格式化。

格式化用户数据后,将 JSON 数据通过 HTTPS 请求发送到用户导入 API 的 _api/admin/users/import 端点。

零停机迁移中导入用户的步骤与批量迁移中相同。

第 3 步:实现并部署更新后的中间件与服务端代码

实现能够同时识别 Authany 和旧身份验证提供方所签发会话的中间件与服务端代码。

您的实现应能够将旧系统中的访问令牌和委托标识符(登录 ID)发送到本指南稍后实现的迁移端点。

const retrieveSessionFromOldSystem = () => {
    // ... implement the logic for retrieving session from old system here.
    // also you can return the user's attribute from the old system that was used as identifier in your import.
    // for example, if "identifier": "email", return the user's email. this will be used as loginID to retrieve the user's ID from Authgear

    return {
        loginID: "user@example.com",
        accessToken: "ey..."
    };
}

第 4 步:实现迁移端点

现在,在您的应用中实现一个端点,用于验证来自旧系统的访问令牌;如果令牌有效,则继续后续处理。

本指南中 Express.js 演示应用的迁移端点使用用户的邮箱地址作为登录 ID,这是因为我们假设已将 email 设置为标识符。该标识符是在向 Authany 批量导入用户数据时设置的。如果您将 phone 设置为标识符,则可以改用用户的手机号作为登录 ID。

说明

在本步骤剩余部分,我们将向 Admin API 的 GraphQL 端点发送 HTTPS 请求,这需要一个有效的 Admin API JWT。详情请参阅验证 Admin API。

验证访问令牌后,使用该用户的登录 ID 调用 Admin API 的 getUserByLoginID 查询。

const admin_api_endpoint = appUrl + "/_api/admin/graphql";
    const getUserQuery = await axios.post(
        admin_api_endpoint,
        {
          query: `query {
            getUserByLoginID(loginIDKey: "email", loginIDValue: "user@example.com")
            {
              id
            }
          }`,
        },
        {
          headers: {
            'Content-Type': 'application/json',
            Authorization: "Bearer " + adminJWT,

          }  
        }
      );

      const userID = getUserQuery.data.data.getUserByLoginID.id;

在 getUserByLoginID 查询的输入中,分别使用 loginIDKey 和 loginIDValue 指定您的标识符类型和登录 ID。

getUserByLoginID 查询会在 data.getUserByLoginID.id 中返回当前用户 sub(用户 ID)的 base64 编码版本。您将在 createSession mutation 中使用该用户 ID。

获得有效的 base64 编码用户 ID 后,调用 createSession mutation 以生成新的 Authany 会话(包含访问令牌和刷新令牌)。

//now create a new session
const createSessionMutation = await axios.post(
  admin_api_endpoint,
  {
    query: `mutation {
      createSession(input: {userID: "${userID}", clientID: "<YOUR_AUTHGEAR_CLIENT_ID>"})
      {
        accessToken,
        expiresIn,
        refreshToken
      }
    }`,
  },
  {
    headers: {
      'Content-Type': 'application/json',
      Authorization: "Bearer " + adminJWT,

    }  
  }
);

const authgearAccessToken = createSessionMutation.data.data.createSession.accessToken;

现在,您可以使用 Authany 访问令牌保护应用中的资源。由于迁移端点会在不要求用户重新登录的情况下生成新的 Authany 会话,所有用户都可以不受任何干扰地继续使用更新后的应用。