# 设置自定义后端

{% hint style="warning" %}
本指南将引导你完成为你的文档设置受保护的登录界面。在阅读本指南之前，请先完成以下流程： [启用身份验证访问](https://gitbook-v2-2miaz89w9-gitbook.vercel.app/url/gitbook.com/docs/documentation/zh/site-access/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-2miaz89w9-gitbook.vercel.app/url/gitbook.com/docs/documentation/zh/site-access/authenticated-access/setting-up-auth0) | [Azure AD](https://gitbook-v2-2miaz89w9-gitbook.vercel.app/url/gitbook.com/docs/documentation/zh/site-access/authenticated-access/setting-up-azure-ad) | [Okta](https://gitbook-v2-2miaz89w9-gitbook.vercel.app/url/gitbook.com/docs/documentation/zh/site-access/authenticated-access/setting-up-okta) | [AWS Cognito](https://gitbook-v2-2miaz89w9-gitbook.vercel.app/url/gitbook.com/docs/documentation/zh/site-access/authenticated-access/setting-up-aws-cognito) | [OIDC](https://gitbook-v2-2miaz89w9-gitbook.vercel.app/url/gitbook.com/docs/documentation/zh/site-access/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，当未认证访客访问你的网站时将使用该 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 Token（JWT）** ，在身份验证成功后。
* 将用户重定向到 GitBook，并在 URL 中包含 JWT。

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

一旦你的后端完成用户身份验证，它必须 **生成一个 JWT** 并在 **将其传递给 GitBook** 时 **重定向** 他们到你的站点。该令牌应使用 **私钥** 进行签名，该私钥可在你站点的受众设置中找到，在 [启用身份验证访问](https://gitbook-v2-2miaz89w9-gitbook.vercel.app/url/gitbook.com/docs/documentation/zh/site-access/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>

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

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

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

这对于页眉链接以及其他入口点很有用，你希望用户登录后返回到同一页面。

当重定向到回退 URL 时，GitBook 会在回退 URL 中包含一个 `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` search 参数 - 你不能在回退 URL 中使用它。例如， `https://auth.gitbook.com/?location=something` 不是一个有效的回退 URL。
{% endhint %}

#### 使用 GitBook 的注销端点

如果你想在发布的网站上提供注销链接，请链接到 `<publishedSiteURL>/~gitbook/auth/logout`.

此端点会让访客退出其 GitBook 会话。

### 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 指向你的认证后端，由它负责对用户进行身份验证并将用户重定向回所请求的内容。

为了支持多个租户，你的认证后端需要知道用户要访问的是哪个 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 负载中包含额外的用户属性（声明），并在将用户重定向到站点时将其包含在 URL 中。

当这些声明包含在 JWT 中时，GitBook 会使用它们来 [自适应内容](https://gitbook-v2-2miaz89w9-gitbook.vercel.app/url/gitbook.com/docs/documentation/zh/site-access/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));
    
    // 将用户重定向到 GitBook，并在 URL 中包含 JWT 令牌
    const redirectURL = `${GITBOOK_DOCS_URL}/?jwt_token=${gitbookVisitorJWT}`;
    res.redirect(redirectURL);
}
```

{% endcode %}

在设置好并配置要发送给 GitBook 的正确声明后，前往“[调整你的内容](https://gitbook-v2-2miaz89w9-gitbook.vercel.app/url/gitbook.com/docs/documentation/zh/site-access/adaptive-content/adapting-your-content)”继续配置你的站点。
