Files
SnapOtter/apps/docs/zh-CN/guide/saml.md
T

225 lines
12 KiB
Markdown
Raw Normal View History

---
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 要求响应和断言都必须签名)