Files
SnapOtter/apps/docs/zh-CN/guide/saml.md
T
SnapOtterandGitHub 4963ab3bbd feat(docs-i18n): translate all documentation into 20 languages
All 181 docs markdown files translated into 20 languages (apps/docs/<locale>/**). Companion to the i18n code PR; admin-merged because the file count exceeds GitHub's per-PR CI trigger limit. Validated by pnpm i18n:check (all surfaces, 0 stale/missing) and a clean all-locale docs build.
2026-07-11 13:52:47 +08:00

225 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
description: "为 SnapOtter 设置 SAML 2.0 单点登录。提供 Okta、Azure AD / Entra ID、Google Workspace 及其他 SAML 身份提供商的分步指南。"
i18n_source_hash: 33dfb8b02a22
i18n_provenance: human
i18n_output_hash: 75d4acf29255
---
# SAML SSO {#saml-sso}
SnapOtter 支持使用 SAML 2.0 进行单点登录。用户可以通过外部身份提供商(Okta、Azure AD / Entra ID、Google Workspace 或任何标准的 SAML 2.0 IdP)登录,作为本地用户名/密码认证的替代方案。
::: tip 企业功能
SAML SSO 需要具备 `saml_sso` 功能的 **团队****企业** 许可证。如果设置了 `SAML_ENABLED=true` 但没有有效的许可证,SAML 路由会被静默跳过,并记录一条警告。
:::
## 前置条件 {#prerequisites}
- 一个可通过公共 URL 访问的运行中的 SnapOtter 实例
-`EXTERNAL_URL` 设置为该公共 URL(例如 `https://photos.example.com`
- 具备 `saml_sso` 功能的团队或企业许可证密钥
- 你的 SAML 身份提供商的管理员访问权限
## 快速开始 {#quick-start}
将这些环境变量添加到你的 `docker-compose.yml` 中:
```yaml
services:
snapotter:
image: snapotter/snapotter:latest
environment:
EXTERNAL_URL: "https://photos.example.com"
SNAPOTTER_LICENSE_KEY: "your-license-key"
SAML_ENABLED: "true"
SAML_IDP_SSO_URL: "https://idp.example.com/sso/saml"
SAML_IDP_CERTIFICATE: |
MIICpDCCAYwCCQDU+pQ4pHgSpDANBgkqhkiG9w0BAQsFADAUMRIw
...your IdP's signing certificate in PEM format...
EAYHKoZIzj0CAQYFK4EEACIDYgAE
```
重启容器。登录页面上会出现一个“使用 SAML 登录”按钮(或由 `SAML_PROVIDER_NAME` 设置的标签)。
## 配置参考 {#configuration-reference}
| 变量 | 默认值 | 说明 |
|---|---|---|
| `SAML_ENABLED` | `false` | 启用 SAML 登录。 |
| `SAML_IDP_SSO_URL` | | IdP 的 SSO 端点 URL。启用 SAML 时**必填**。 |
| `SAML_IDP_CERTIFICATE` | | IdP 的 PEM 格式 X.509 签名证书(证书文本本身,而非文件路径)。启用 SAML 时**必填**。 |
| `EXTERNAL_URL` | | SnapOtter 可访问的公共 URL。启用 SAML 时**必填**。 |
| `SAML_ENTITY_ID` | `${EXTERNAL_URL}/api/auth/saml/metadata` | 发送给 IdP 的 SP Entity ID / Audience URI。 |
| `SAML_CALLBACK_URL` | `${EXTERNAL_URL}/api/auth/saml/callback` | 断言消费者服务(ACS)URL。 |
| `SAML_AUTO_CREATE_USERS` | `true` | 在首次 SAML 登录时自动创建本地用户账户。 |
| `SAML_AUTO_LINK_USERS` | `false` | 如果电子邮件地址匹配,则将 SAML 身份链接到现有的本地用户。 |
| `SAML_DEFAULT_ROLE` | `user` | 分配给自动创建的 SAML 用户的角色。为 `admin``editor``user` 之一。 |
| `SAML_PROVIDER_NAME` | | 前端 SAML 登录按钮的显示标签(例如“Okta”、“Azure AD”)。如果为空,按钮显示“SAML”。 |
| `SAML_USERNAME_ATTRIBUTE` | | 用作用户名的 SAML 断言属性。如果为空,则回退为电子邮件的本地部分,然后是 NameID。 |
| `SAML_EMAIL_ATTRIBUTE` | `email` | 用作用户电子邮件地址的 SAML 断言属性。 |
如果 `SAML_ENABLED=true` 而三个必填变量(`SAML_IDP_SSO_URL``SAML_IDP_CERTIFICATE``EXTERNAL_URL`)中有任何一个缺失,服务器将拒绝启动。
::: details 安全说明
`wantAuthnResponseSigned``wantAssertionsSigned` 都被硬编码为 `true`。SnapOtter 会拒绝未签名或签名不当的 SAML 响应。来自受信任 IdP 的断言被视为已验证电子邮件。
仅支持 SP 发起的登录。SnapOtter 不支持 IdP 发起的(未经请求的)登录或单点登出(SLO)。登出 SnapOtter 不会将用户从 IdP 登出。
:::
## SP 元数据和 URL {#sp-metadata-and-urls}
你的 IdP 需要来自 SnapOtter 的三个值:
| 字段 | 值 |
|---|---|
| **ACS URL**(断言消费者服务) | `${EXTERNAL_URL}/api/auth/saml/callback` |
| **Entity ID** / **Audience URI** | `${EXTERNAL_URL}/api/auth/saml/metadata` |
| **SP 元数据**XML | `GET ${EXTERNAL_URL}/api/auth/saml/metadata` |
例如,如果 `EXTERNAL_URL``https://photos.example.com`
- ACS URL`https://photos.example.com/api/auth/saml/callback`
- Entity ID`https://photos.example.com/api/auth/saml/metadata`
- 元数据端点:`https://photos.example.com/api/auth/saml/metadata`(返回 XML
有些 IdP 可以直接导入 SP 元数据 URL,从而自动填充 ACS URL 和 Entity ID。
## 提供商设置 {#provider-setup}
### Okta {#okta}
1. 在 Okta 管理控制台中,进入 **Applications > Create App Integration**
2. 选择 **SAML 2.0** 并点击 **Next**
3. 设置一个名称(例如“SnapOtter”)并点击 **Next**
4. 配置 SAML 设置:
- **Single sign-on URL**:你的 ACS URL(例如 `https://photos.example.com/api/auth/saml/callback`
- **Audience URI (SP Entity ID)**:你的 Entity ID(例如 `https://photos.example.com/api/auth/saml/metadata`
- **Name ID format**EmailAddress
- **Application username**Email
5.**Attribute Statements** 下,添加 `email` 并映射到 `user.email`
6. 点击 **Next**,然后点击 **Finish**
7. 进入 **Sign On** 选项卡,点击 **View SAML setup instructions**,并复制:
- **Identity Provider Single Sign-On URL** 到 `SAML_IDP_SSO_URL`
- **X.509 Certificate** 到 `SAML_IDP_CERTIFICATE`
### Azure AD / Entra ID {#azure-ad-entra-id}
1. 在 Azure 门户中,进入 **Microsoft Entra ID > Enterprise applications > New application**
2. 点击 **Create your own application**,将其命名为“SnapOtter”,并选择 **Integrate any other application you don't find in the gallery**
3. 进入 **Single sign-on > SAML**,并在 **Basic SAML Configuration** 部分点击 **Edit**
- **Identifier (Entity ID)**:你的 Entity ID(例如 `https://photos.example.com/api/auth/saml/metadata`
- **Reply URL (ACS URL)**:你的 ACS URL(例如 `https://photos.example.com/api/auth/saml/callback`
4.**SAML Certificates** 下,下载 **Certificate (Base64)**
5.**Set up SnapOtter** 下,复制 **Login URL**
6.`SAML_IDP_SSO_URL` 设置为该 Login URL,并将 `SAML_IDP_CERTIFICATE` 设置为所下载的证书内容。
7.**Users and groups** 下将用户或组分配给该应用。
### Google Workspace {#google-workspace}
1. 在 Google 管理控制台中,进入 **Apps > Web and mobile apps > Add app > Add custom SAML app**
2. 将该应用命名为“SnapOtter”并点击 **Continue**
3.**Google Identity Provider details** 页面,复制 **SSO URL** 并下载 **Certificate**。点击 **Continue**
4. 配置 Service Provider 详情:
- **ACS URL**:你的 ACS URL(例如 `https://photos.example.com/api/auth/saml/callback`
- **Entity ID**:你的 Entity ID(例如 `https://photos.example.com/api/auth/saml/metadata`
- **Name ID format**EMAIL
- **Name ID**Basic Information > Primary email
5. 点击 **Continue**,然后点击 **Finish**
6. 为你的组织单位将该应用设为 **ON**
7.`SAML_IDP_SSO_URL` 设置为步骤 3 中的 SSO URL,并将 `SAML_IDP_CERTIFICATE` 设置为所下载的证书内容。
### 通用 SAML 2.0 IdP {#generic-saml-2-0-idp}
对于任何符合 SAML 2.0 的身份提供商:
1. 在你的 IdP 中创建一个新的 SAML 应用/服务提供商。
2.**ACS URL** 设置为 `${EXTERNAL_URL}/api/auth/saml/callback`
3.**Entity ID** / **Audience** 设置为 `${EXTERNAL_URL}/api/auth/saml/metadata`
4. 配置 IdP 在名为 `email` 的属性中发送用户的电子邮件(或设置 `SAML_EMAIL_ATTRIBUTE` 以匹配你的 IdP 的属性名)。
5.**IdP SSO URL****签名证书** 复制到 `SAML_IDP_SSO_URL``SAML_IDP_CERTIFICATE`
## 用户预置 {#user-provisioning}
### 自动创建 {#auto-create}
`SAML_AUTO_CREATE_USERS``true`(默认)时,会在某人首次通过 SAML 登录时创建一个本地用户账户。角色设置为 `SAML_DEFAULT_ROLE`
用户名按以下顺序派生:
1. `SAML_USERNAME_ATTRIBUTE` 指定的断言属性的值(如果已设置且存在)
2. 电子邮件地址的本地部分(`@` 之前的所有内容)
3. SAML NameID
如果发生用户名冲突,会追加一个数字后缀(例如 `jane` 变为 `jane_2`)。
### 自动链接 {#auto-link}
`SAML_AUTO_LINK_USERS``true` 时,如果电子邮件地址匹配,SnapOtter 会将 SAML 身份链接到现有的本地账户。当你已预先创建用户账户,并希望他们在不丢失数据的情况下开始使用 SSO 时,这很有用。
::: warning
只有在你信任 SAML IdP 会验证电子邮件地址的情况下,才启用自动链接。来自配置错误的 IdP 的未经验证的电子邮件可能会让某人接管另一个用户的账户。
:::
### 属性映射 {#attribute-mapping}
| SnapOtter 字段 | 来源 | 配置 |
|---|---|---|
| 电子邮件 | 断言属性 | `SAML_EMAIL_ATTRIBUTE`(默认:`email` |
| 用户名 | 断言属性、电子邮件或 NameID | `SAML_USERNAME_ATTRIBUTE`(见上文派生顺序) |
| 外部 ID | NameID | 始终为 SAML NameID,不可配置 |
## SSO 强制 {#sso-enforcement}
如果你希望要求所有用户都通过 SAML(或 OIDC)登录并阻止本地密码登录,请启用 SSO 强制:
1. 确保 `sso_enforcement` 企业功能已获授权(在团队和企业方案上可用)。
2.**Admin Settings > Security** 中,打开 **SSO Enforcement**
3. 设置一个 **应急用户名**:这是唯一一个在 IdP 无法访问时仍可使用密码登录以进行紧急访问的本地账户。
当 SSO 强制处于激活状态时,任何本地登录尝试(应急用户除外)都会返回 403 错误,消息为“Local password login is disabled. Please use SSO.”
::: tip
在启用 SSO 强制之前,请务必先配置一个应急用户名。否则,如果你的 IdP 宕机,你可能会被锁在 SnapOtter 之外。
:::
## 将 SAML 与 OIDC 并用 {#using-saml-alongside-oidc}
SAML 和 OIDC 可以同时启用。当两者都处于激活状态时,登录页面会为每个提供商显示各自的按钮(由 `SAML_PROVIDER_NAME``OIDC_PROVIDER_NAME` 标注)。用户可以使用任一方式登录。
两个提供商各自独立地共享相同的自动创建、自动链接和 SSO 强制设置:每个都有自己的 `*_AUTO_CREATE_USERS``*_AUTO_LINK_USERS``*_DEFAULT_ROLE` 变量。
## 故障排查 {#troubleshooting}
### 断言验证失败 {#assertion-validation-failed}
无法验证 SAML 响应签名或断言签名。请检查:
- `SAML_IDP_CERTIFICATE` 中的证书与你的 IdP 中当前的签名证书是否匹配(证书会轮换,因此请检查是否过期)
- 证书是否为 PEM 格式(以 `-----BEGIN CERTIFICATE-----` 开头)
- 证书是否为完整文本,而非文件路径
- 你的 IdP 中配置的 ACS URL 和 Entity ID 是否与 SnapOtter 的值完全一致(scheme、host、port、path
### 缺少属性 {#missing-attributes}
如果登录后用户名或电子邮件为空,你的 IdP 可能没有发送预期的属性。请检查:
- 你的 IdP 已配置为释放一个 `email` 属性(或 `SAML_EMAIL_ATTRIBUTE` 所设置的值)
- 如果使用 `SAML_USERNAME_ATTRIBUTE`,请验证该属性已包含在断言中
- 有些 IdP 需要显式的属性映射配置才能释放声明
### 时钟偏差 {#clock-skew}
SAML 断言包含时间戳条件(`NotBefore``NotOnOrAfter`)。如果你的服务器时钟与 IdP 时钟不同步,断言验证会失败。在两台机器上都运行 NTP 以保持时钟对齐。
### “SAML is enabled via env but saml_sso enterprise feature is not licensed” {#saml-is-enabled-via-env-but-saml-sso-enterprise-feature-is-not-licensed}
`SAML_ENABLED=true` 但许可证不包含 `saml_sso` 功能时,服务器日志中会出现此警告。请核对你的许可证密钥和方案。`saml_sso` 功能在团队和企业方案上可用。
### 登录重定向回来并带有错误 {#login-redirects-back-with-error}
如果点击 SAML 登录按钮后重定向回登录页面并带有错误,请查看服务器日志以了解详情。常见原因:
- 从服务器无法访问 IdP SSO URL
- IdP 拒绝了认证请求(请查看 IdP 的审计日志)
- IdP 返回了未签名的响应(SnapOtter 要求响应和断言都必须签名)