# 设置自定义后端

{% hint style="warning" %}
本指南将带你完成为你的文档设置受保护的登录界面的过程。在继续本指南之前，请确保你已经先完成了 [启用经过身份验证的访问](https://gitbook-open-v2-preview.gitbook.workers.dev/url/gitbook.com/docs/documentation/zh/publishing-documentation/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-open-v2-preview.gitbook.workers.dev/url/gitbook.com/docs/documentation/zh/publishing-documentation/authenticated-access/setting-up-auth0) | [Azure AD](https://gitbook-open-v2-preview.gitbook.workers.dev/url/gitbook.com/docs/documentation/zh/publishing-documentation/authenticated-access/setting-up-azure-ad) | [Okta](https://gitbook-open-v2-preview.gitbook.workers.dev/url/gitbook.com/docs/documentation/zh/publishing-documentation/authenticated-access/setting-up-okta) | [AWS Cognito](https://gitbook-open-v2-preview.gitbook.workers.dev/url/gitbook.com/docs/documentation/zh/publishing-documentation/authenticated-access/setting-up-aws-cognito) | [OIDC](https://gitbook-open-v2-preview.gitbook.workers.dev/url/gitbook.com/docs/documentation/zh/publishing-documentation/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-fallback-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）** ，在身份验证成功后。
* 将包含 JWT 的 URL 重定向给 GitBook。

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

一旦你的后端验证了用户身份，它必须 **生成一个 JWT** 并在 **将其传递给 GitBook** 时 **重定向** 到你的网站。该令牌应使用以下内容进行签名： **私钥** 这是在你的网站受众设置中提供的，位于 [启用经过身份验证的访问](https://gitbook-open-v2-preview.gitbook.workers.dev/url/gitbook.com/docs/documentation/zh/publishing-documentation/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));
    
    // 将用户重定向到 GitBook，并在 URL 中包含 JWT 令牌
    const redirectURL = `${GITBOOK_DOCS_URL}/?jwt_token=${gitbookVisitorJWT}`;
    res.redirect(redirectURL);
}
```

{% endcode %}

### 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 fallback URL"><figcaption><p>配置回退 URL</p></figcaption></figure>

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

```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 %}

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

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

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

你的身份验证后端需要知道你希望它处理的所有 GitBook 站点的 JWT 签名密钥和 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 指向你的身份验证后端，后者负责对用户进行身份验证并将其重定向回请求的内容。

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

例如，你可以按如下方式为每个站点设置回退 URL：

<table><thead><tr><th width="150.75390625">GitBook 站点</th><th>回退 URL</th></tr></thead><tbody><tr><td>Customer A 站点</td><td><code>https://auth-backend.acme.org/login?site=customer-a</code></td></tr><tr><td>Customer B 站点</td><td><code>https://auth-backend.acme.org/login?site=customer-b</code></td></tr></tbody></table>

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

```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 载荷中包含额外的用户属性（claims），并在将用户重定向到站点时将其包含在 URL 中。

当这些 claims 包含在 JWT 中时，GitBook 会使用它们来 [自适应内容](https://gitbook-open-v2-preview.gitbook.workers.dev/url/gitbook.com/docs/documentation/zh/publishing-documentation/adaptive-content/adapting-your-content) ，以动态方式服务于你站点的访客。

综上所述，以下代码示例展示了你可以如何在 JWT 中包含这些 claims，随后 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，并将用户属性作为 claims 包含进去
    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));
    
    // 将用户重定向到 GitBook，并在 URL 中包含 JWT 令牌
    const redirectURL = `${GITBOOK_DOCS_URL}/?jwt_token=${gitbookVisitorJWT}`;
    res.redirect(redirectURL);
}
```

{% endcode %}

在设置并配置好要发送给 GitBook 的正确 claims 后，前往“[调整你的内容](https://gitbook-open-v2-preview.gitbook.workers.dev/url/gitbook.com/docs/documentation/zh/publishing-documentation/adaptive-content/adapting-your-content)”以继续配置你的网站。
