# 设置自定义后端

{% hint style="warning" %}
本指南将带你设置一个用于文档的受保护登录界面。在继续本指南之前，请确保你已经先完成了以下流程： [启用经过身份验证的访问](https://gitbook-v2-lrp5kto0w-gitbook.vercel.app/url/gitbook.com/docs/documentation/zh/zhan-dian-fang-wen/authenticated-access/enabling-authenticated-access).
{% endhint %}

本指南将带你通过使用你自己的 **自定义** 身份验证后端来为你的 GitBook 文档站点设置一个受保护的登录界面。

{% hint style="info" %}
如果你使用的是我们支持的身份验证提供商之一，或者你有一个 [OpenID Connect](https://auth0.com/docs/authenticate/protocols/openid-connect-protocol) （OIDC）兼容后端，请查看我们的集成指南，以获得更简化的设置：\
\
[Auth0](https://gitbook-v2-lrp5kto0w-gitbook.vercel.app/url/gitbook.com/docs/documentation/zh/zhan-dian-fang-wen/authenticated-access/setting-up-auth0) | [Azure AD](https://gitbook-v2-lrp5kto0w-gitbook.vercel.app/url/gitbook.com/docs/documentation/zh/zhan-dian-fang-wen/authenticated-access/setting-up-azure-ad) | [Okta](https://gitbook-v2-lrp5kto0w-gitbook.vercel.app/url/gitbook.com/docs/documentation/zh/zhan-dian-fang-wen/authenticated-access/setting-up-okta) | [AWS Cognito](https://gitbook-v2-lrp5kto0w-gitbook.vercel.app/url/gitbook.com/docs/documentation/zh/zhan-dian-fang-wen/authenticated-access/setting-up-aws-cognito) | [OIDC](https://gitbook-v2-lrp5kto0w-gitbook.vercel.app/url/gitbook.com/docs/documentation/zh/zhan-dian-fang-wen/authenticated-access/setting-up-oidc)
{% endhint %}

### 概览

要为你的 GitBook 站点设置自定义身份验证系统，请遵循以下关键步骤：

{% stepper %}
{% step %}
[**创建一个自定义后端来验证你的用户身份**](#id-1.-create-a-custom-backend-to-authenticate-your-users)

实现一个会提示用户登录并对其进行身份验证的后端。
{% endstep %}

{% step %}
[**签名并将 JWT 令牌传递给 GitBook**](#id-2.-sign-and-pass-a-jwt-token-to-gitbook)

创建一个 JWT 令牌，并使用你站点的私钥对其进行签名。
{% endstep %}

{% step %}
[**配置登录 URL**](#id-3.-configure-a-login-url)

配置一个在未经过身份验证的访问者访问你的网站时使用的 URL。
{% endstep %}

{% step %}
[**设置多租户身份验证访问（可选）**](#id-4.-set-up-multi-tenant-authenticated-access)

配置你的后端以处理跨多个 GitBook 站点的身份验证。
{% endstep %}

{% step %}
[**为自适应内容配置你的后端（可选）**](#id-5.-configure-your-backend-for-adaptive-content)

配置你的后端以与 GitBook 中的自适应内容配合使用。
{% endstep %}
{% endstepper %}

### 1. 创建一个自定义后端来验证你的用户身份

为了在用户访问你的文档之前开始对其进行身份验证，你需要设置一个能够处理用户登录和身份验证的服务器。

你的后端应当：

* 提示用户使用你偏好的身份验证方式登录。
* 验证用户凭据并对其进行身份验证。
* 生成并签署一个 **JSON Web 令牌（JWT）** ，在身份验证成功后。
* 将用户重定向到 GitBook，并在 URL 中包含 JWT。

### 2. 签名并将 JWT 令牌传递给 GitBook

一旦你的后端验证了用户身份，它必须 **生成一个 JWT** 并 **将其传递给 GitBook** 当 **重定向** 他们到你的网站时。该令牌应使用 **私钥** 进行签名，该私钥在你的网站受众设置中提供于 [启用经过身份验证的访问](https://gitbook-v2-lrp5kto0w-gitbook.vercel.app/url/gitbook.com/docs/documentation/zh/zhan-dian-fang-wen/enabling-authenticated-access#enable-authenticated-access).

以下示例应展示你的自定义后端中的登录请求处理程序可能是什么样子：

{% code title="index.ts" %}

```typescript
import { Request, Response } from 'express';
import * as jose from 'jose';

import { getUserInfo } from '../services/user-info-service';
import { getFeatureFlags } from '../services/feature-flags-service';

const GITBOOK_VISITOR_SIGNING_KEY = process.env.GITBOOK_VISITOR_SIGNING_KEY!;
const GITBOOK_DOCS_URL = 'https://mycompany.gitbook.io/myspace';

export async function handleAppLoginRequest(req: Request, res: Response) {
    // 处理登录请求的业务逻辑
    // 例如，检查凭据并验证用户身份
    //
    // 例如：
    // const loggedInUser = await authenticateUser(req.body.username, req.body.password);
    
    // 生成一个已签名的 JWT
    const gitbookVisitorJWT = await new jose.SignJWT({})
        .setProtectedHeader({ alg: 'HS256' })
        .setIssuedAt()
        .setExpirationTime('2h') // 任意设置的 2 小时过期时间
        .sign(new TextEncoder().encode(GITBOOK_VISITOR_SIGNING_KEY));
    
    // 使用 URL 中的 JWT 令牌将用户重定向到 GitBook
    const redirectURL = `${GITBOOK_DOCS_URL}/?jwt_token=${gitbookVisitorJWT}`;
    res.redirect(redirectURL);
}
```

{% endcode %}

#### 使访客退出其 GitBook 会话

要将访客从其 GitBook 会话中注销，请将他们重定向到你的网站 URL，并附加 `~gitbook/auth/logout` ：

`https://mycompany.gitbook.io/myspace/~gitbook/auth/logout`

此端点只会将访客从 GitBook 中注销。如果你还想让他们从你自己的身份提供商中注销，请在你自己的注销流程中单独处理。

### 3. 配置登录 URL

当未经过身份验证的访客尝试访问你的受保护站点时，会使用登录 URL。然后 GitBook 会将他们重定向到这个 URL。

此 URL 应指向你自定义后端中的一个处理程序，在那里你可以提示他们登录、进行身份验证，然后将他们重定向回你的网站，并在 URL 中包含 JWT。

例如，如果你的登录界面位于 `https://example.com/login`，你应将此值作为登录 URL。

你可以在网站受众设置中的“已验证访问”选项卡下配置此登录 URL。

<figure><img src="https://2111890564-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FNkEGS7hzeqa35sMXQZ4X%2Fuploads%2FB48PEdMz1tCDf0Q0lo4d%2FScreenshot%202025-03-25%20at%2015.00.08.png?alt=media&#x26;token=e22fe867-e1f6-44f7-8b4f-a868ac620464" alt="A GitBook screenshot showing where to configure a login URL"><figcaption><p>配置登录 URL</p></figcaption></figure>

#### 使用 GitBook 的登录端点

如果你想在已发布的网站上提供一个登录链接，请链接到 `<publishedSiteURL>/~gitbook/auth/login`.

此端点会将访问者重定向到为该站点配置的身份验证后端。它还会添加一个 `location` 查询参数，该参数与他们开始访问的页面相匹配。

这对于页眉链接和其他入口点很有用，你希望访客登录后返回同一页面时尤其如此。

当重定向到登录 URL 时，GitBook 会包含一个 `location` 查询参数，你可以在处理程序中利用它将用户重定向回原始位置：

```javascript
const gitbookVisitorJWT = await new jose.SignJWT({})
    .setProtectedHeader({ alg: 'HS256' })
    .setIssuedAt()
    .setExpirationTime('2h') // 任意设置的 2 小时过期时间
    .sign(new TextEncoder().encode(GITBOOK_VISITOR_SIGNING_KEY));
    
// 将用户重定向到原始的 GitBook 文档 URL，并将 JWT 作为 jwt_token 查询参数包含其中
// 如果提供了 location，用户将被重定向回其原始目的地
const redirectURL = `${GITBOOK_DOCS_URL}/${req.query.location || ''}?jwt_token=${gitbookVisitorJWT}`;
res.redirect(redirectURL);
```

{% hint style="warning" %}
因为 GitBook 依赖于 `location` 查询参数——你不能在登录 URL 中使用它。例如， `https://auth.gitbook.com/?location=something` 不是有效的登录 URL。
{% endhint %}

#### 使用 GitBook 的登出端点

如果你想在已发布的网站上提供一个退出登录链接，请链接到 `<publishedSiteURL>/~gitbook/auth/logout`.

此端点会将访客从其 GitBook 会话中注销。

### 4. 设置多租户身份验证访问（可选）

如果你使用 GitBook 作为向不同客户提供内容的平台，你很可能需要设置多租户身份验证访问。你的身份验证后端需要负责处理跨多个不同站点的身份验证。在 GitBook 中，只需对你的自定义身份验证后端代码做一些小调整即可实现这一点。

#### 将所有租户添加到你的身份验证服务器中

你的身份验证后端需要知道 JWT 签名密钥以及它预计要处理的所有 GitBook 站点的 URL。如果你的组织中有两个站点，分别面向 Customer A 和 Customer B，你可以想象你的身份验证代码会存储这样的映射：

```typescript
const CUSTOMER_A = {
  jwtSigningKey: 'aaa-aaa-aaa-aaa',
  url: 'https://mycompany.gitbook.io/customer-a'
};

const CUSTOMER_B = {
  jwtSigningKey: 'bbb-bbb-bbb-bbb',
  url: 'https://mycompany.gitbook.io/customer-b'
};
```

#### 为你的身份验证服务器提供额外上下文

当 GitBook 无法验证用户请求时，它会将其重定向到登录 URL。此 URL 指向你的身份验证后端，由其负责验证用户身份并将其重定向回请求的内容。

为了支持多个租户，你的身份验证后端需要知道用户打算访问的是哪个 GitBook 站点。此信息可以通过登录 URL 传递。

因此，例如，你可以按如下方式为每个站点设置登录 URL：

然后你的身份验证后端就可以检查这些信息，并据此处理到正确站点的重定向：

```javascript
const customerInfo = req.query.site === 'customer-a' ? CUSTOMER_A : CUSTOMER_B;
  
const gitbookVisitorJWT = await new jose.SignJWT({})
    .setProtectedHeader({ alg: 'HS256' })
    .setIssuedAt()
    .setExpirationTime('2h') // 任意设置的 2 小时过期时间
    .sign(new TextEncoder().encode(customerInfo.jwtSigningKey));
    
// 将用户重定向到原始的 GitBook 文档 URL，并将 JWT 作为 jwt_token 查询参数包含其中
// 如果提供了 location，用户将被重定向回其原始目的地
const redirectURL = `${customerInfo.url}/${req.query.location || ''}?jwt_token=${gitbookVisitorJWT}`;
res.redirect(redirectURL);
```

### 5. 为自适应内容配置你的后端（可选）

为了在你的身份验证访问设置中利用自适应内容功能，你可以在自定义后端生成的 JWT 负载中包含额外的用户属性（声明），并在将用户重定向到站点时将其包含在 URL 中。

当这些声明包含在 JWT 中时，GitBook 会使用它们来 [适配内容](https://gitbook-v2-lrp5kto0w-gitbook.vercel.app/url/gitbook.com/docs/documentation/zh/zhan-dian-fang-wen/adaptive-content/adapting-your-content) ，以便动态地为你的网站访客提供内容。

综合起来，下面的代码示例演示了你可以如何在 JWT 中包含这些声明，随后 GitBook 可使用它们为你的访客适配内容：

{% code title="index.ts" %}

```typescript
import { Request, Response } from 'express';
import * as jose from 'jose';

import { getUserInfo } from '../services/user-info-service';
import { getFeatureFlags } from '../services/feature-flags-service';

const GITBOOK_VISITOR_SIGNING_KEY = process.env.GITBOOK_VISITOR_SIGNING_KEY!;
const GITBOOK_DOCS_URL = 'https://mycompany.gitbook.io/myspace';

export async function handleAppLoginRequest(req: Request, res: Response) {
    // 处理登录请求的业务逻辑
    // 例如，检查凭据并验证用户身份
    //
    // 例如：
    // const loggedInUser = await authenticateUser(req.body.username, req.body.password);
    
    // 为了便于这个示例，假设有一个已登录用户对象
    const loggedInUser = { id: '12345' }; // 替换为实际的身份验证逻辑

    // 检索要传递给 GitBook 的用户信息
    const userInfo = await getUserInfo(loggedInUser.id);
    
    // 生成一个已签名的 JWT，并将用户属性作为声明包含其中
    const gitbookVisitorClaims = {
        firstName: userInfo.firstName,
        lastName: userInfo.lastName,
        isBetaUser: userInfo.isBetaUser,
        products: userInfo.products.map((product) => product.name),
        featureFlags: await getFeatureFlags({ userId: loggedInUser.id })
    };
    
    const gitbookVisitorJWT = await new jose.SignJWT(gitbookVisitorClaims)
        .setProtectedHeader({ alg: 'HS256' })
        .setIssuedAt()
        .setExpirationTime('2h') // 任意设置的 2 小时过期时间
        .sign(new TextEncoder().encode(GITBOOK_VISITOR_SIGNING_KEY));
    
    // 使用 URL 中的 JWT 令牌将用户重定向到 GitBook
    const redirectURL = `${GITBOOK_DOCS_URL}/?jwt_token=${gitbookVisitorJWT}`;
    res.redirect(redirectURL);
}
```

{% endcode %}

在设置并配置好要发送给 GitBook 的正确声明后，前往“[适配你的内容](https://gitbook-v2-lrp5kto0w-gitbook.vercel.app/url/gitbook.com/docs/documentation/zh/zhan-dian-fang-wen/adaptive-content/adapting-your-content)”继续配置你的网站。
