配置单点登录
配置 OIDC、SAML 2.0 或 CAS 登录连接并管理 SSO 用户生命周期
登录身份源在站点设置 → 集成(/site-settings/integrations)统一管理:登录身份源、邮件、Webhook 与飞书通讯录共用同一套集成列表,但各自的能力与权限互不混用。先在站点设置 → 安全中打开全局 SSO 策略,再逐个添加登录集成。
每个登录集成的协议配置是一段 JSON,字段随类型不同;保存前先准备好身份提供商那边的回调地址、客户端凭据与证书。
前置条件:
- 全局 SSO 开关已开启,否则登录页不会显示任何 SSO 按钮。
- 操作者具备登录集成的查看或修改权限(超级管理员始终可以)。
- 身份提供商能访问 Clouisle 的后端公网地址,否则回调无法到达(见下一节)。
支持范围
| 类型 | 用途 | 管理位置 |
|---|---|---|
| OAuth2、OIDC、SAML 2.0、CAS | 网页登录 | 站点设置 → 集成中的登录集成对话框 |
| 邮件、Webhook | 通知投递 | 各自的专用表单 |
| 飞书 | 登录与通讯录同步 | 飞书集成卡片(同一套应用凭据,能力分开关控制) |
飞书是一个集成同时提供通讯录同步与网页登录:启用飞书集成本身不会授予登录能力,网页登录需要在同一张卡片上显式打开;关闭集成会同时停用目录同步与登录,但保留这项选择。邮件、Webhook 与飞书为单实例集成,本页只讲登录部分。
回调地址
在身份提供商后台登记的回调(重定向)地址为:
https://your-domain.com/api/v1/sso/callback/<集成名称>your-domain.com是浏览器实际访问到后端的对外地址:经反向代理时使用代理转发的对外地址;若前端代理了接口路径,则使用前端公开域名。它不是服务内部地址,也不是前端页面地址——用错会导致回调失败或跳回错误域名。- 按上面的格式拼出地址(域名 + 集成名称);飞书集成卡片会直接显示可复制的重定向地址。
<集成名称>必须与 Clouisle 中登记的集成名称完全一致,大小写敏感,且创建后保持稳定。回调地址不匹配是 SSO 登录失败最常见的原因。
添加登录集成
在站点设置 → 集成点击添加登录集成,对话框分 Basic Info / Configuration / Attribute Mapping 三个页签。基本信息:
| 字段 | 必填 | 约束 |
|---|---|---|
| Provider Name(集成名称) | 是 | 小写字母开头,仅含小写字母、数字、-、_;最长 100;全局唯一(重名会提示名称已存在) |
| Protocol(类型) | 是 | OAuth2 / OIDC / SAML 2.0 / CAS |
| Display Name(显示名称) | 是 | 列表与关联账号卡片中的名称,最长 100 |
| Button Text(按钮文本) | 否 | 登录页按钮文案,最长 50,如「使用 GitHub 继续」 |
| Icon URL(图标) | 否 | 必须是 http:// 或 https:// 开头,最长 512 |
| Enabled(启用) | — | 关闭后该集成不出现在登录页 |
| Allow Signup(允许注册) | — | 允许该集成在首次登录时新建账户 |
| Require Approval(需要审批) | — | 新建的账户需管理员批准后才能登录 |
| Default Role(默认角色) | 否 | 新建账户的初始全局角色;留空时回退到站点默认角色 |
Provider Name 与 Protocol 在创建后不可修改。命名请与身份提供商中的配置保持一致,改名等于重建集成;已有用户身份连接会保留在原集成上。
协议配置
在 Configuration 页签中填写两段 JSON:
- Configuration JSON:协议参数,字段名必须与下表一致;拼写错误不会在保存时报错,而是在登录时暴露为配置异常。
- Credentials JSON:客户端密钥等敏感值,例如
{"client_secret": "..."}。不要把密钥写进 Configuration。
凭据加密存储,保存后永不回显:编辑时看到的是已配置的键名,JSON 里留空表示保留原密钥。
OAuth2 / OIDC
| 字段 | 必填 | 说明 |
|---|---|---|
client_id | 是 | 提供商颁发的 Client ID |
authorization_url | 是 | 授权端点 |
token_url | 是 | 换取令牌的端点 |
userinfo_url | 是 | 用户信息端点(登录时读取的就是它的返回体) |
scopes | 否 | 空格分隔的 scope,默认 openid email profile |
登录使用授权码 + PKCE(S256)流程,并生成 nonce。
当前实现没有 Issuer URL 字段,也不做 ID Token 签名/JWKS 校验:用户信息一律取自 userinfo_url 的响应。因此客户端密钥是否正确、证书是否有效,只有在真正换令牌时才会暴露,见测试连接。
SAML 2.0
| 字段 | 必填 | 说明 |
|---|---|---|
sp_entity_id | 是 | Service Provider Entity ID(本实例标识) |
idp_entity_id | 是 | IdP Entity ID |
sso_url | 是 | IdP 单点登录 URL(HTTP-Redirect 绑定) |
x509_cert | 是 | IdP X.509 证书,PEM 内容但不含 -----BEGIN/END CERTIFICATE----- 头尾 |
acs_url | 是 | ACS 地址;仅作占位/参考,运行时始终使用实际回调地址覆盖 |
slo_url | 否 | SP 单点登出地址 |
idp_slo_url | 否 | IdP 单点登出地址 |
name_id_format | 否 | NameID 格式,默认 urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified |
{
"sp_entity_id": "clouisle",
"idp_entity_id": "https://idp.example.com/metadata",
"sso_url": "https://idp.example.com/sso",
"x509_cert": "MIIC...省略...",
"acs_url": "https://your-domain.com/api/v1/sso/callback/azure-ad"
}开启 strict 模式,SAML 响应必须通过签名校验;IdP 与 Clouisle 的时钟偏差过大会直接判定失败。
slo_url / idp_slo_url 只会被保存,当前没有实现 SP 发起的单点登出流程——用户在 IdP 侧登出不会同步注销 Clouisle 会话。
CAS
| 字段 | 必填 | 说明 |
|---|---|---|
server_url | 是 | CAS 服务器基地址 |
version | 否 | 协议版本 1 / 2 / 3,默认 3,决定校验端点(/validate、/serviceValidate、/p3/serviceValidate) |
service_url | 否 | 不会生效:service 参数始终使用运行时的回调地址 |
CAS 用户标识取自校验响应中的 <cas:user>;版本 3 还会读取 <cas:attributes> 中的属性供属性映射使用。
属性映射
Attribute Mapping 页签是一段 JSON,把提供商返回的数据映射到 Clouisle 需要的字段:
| Clouisle 字段 | 用途 |
|---|---|
email | 账户邮箱(必需,缺失会导致登录失败) |
username | 用户名,重复时会自动加数字后缀 |
avatar_url | 头像地址 |
两种路径写法:
- 点号路径(默认):
{"email": "email", "username": "profile.name"}读取嵌套对象。 - JSONPath:以
$开头,适合数组或过滤,{"email": "$.emails[0].value"};多个匹配时取第一个。
新建集成时的默认值:
{
"email": "email",
"username": "name",
"avatar_url": "picture"
}GitHub 的返回字段与 OIDC 标准不同,需要改成 {"email": "email", "username": "login", "avatar_url": "avatar_url"}。
映射结果会与提供商原始返回字段合并,同名字段以原始值为准。也就是说映射只对「原始返回里不存在同名键」的字段生效;若提供商已返回 email,映射里的 email 会被覆盖。排查字段错位时请先查看原始响应。
最终数据里必须有非空的 email,否则新用户创建会失败并提示邮箱缺失。OIDC 的 sub/id、SAML 的 NameID、CAS 的 <cas:user> 会作为外部身份标识;标识为空同样会导致登录失败。
配置 GitHub 登录
- 确保外部可访问 Clouisle,并保留可用的管理员登录方式。
- 在站点设置 → 安全配置全局 SSO 策略:SSO 总开关、密码登录、自动创建用户、审批和邮箱匹配。
- 在 GitHub 的 Settings → Developer settings → OAuth Apps 创建 OAuth 应用,把回调填写为
https://your-domain.com/api/v1/sso/callback/github。 - 创建名称为
github的 OAuth2 登录集成,填写显示名称、按钮文本、图标,按需打开 Allow Signup / Require Approval 并选择默认角色。 - 在 Configuration 中填写 Client ID、授权 URL、token URL、userinfo URL 与 scope(
read:user user:email);Attribute Mapping 使用{"email": "email", "username": "login", "avatar_url": "avatar_url"}。 - 在 Credentials 中填写
{"client_secret": "..."},不要写进 Configuration。保存并测试后,用独立浏览器会话完成一次真实登录;配置测试通过不代表完整登录已通过。
配置飞书登录
飞书网页登录是飞书集成上的一项可选能力,与通讯录同步共用同一套 App ID / App Secret,无需再创建一个身份源。
- 在飞书开发者后台把飞书卡片中显示的重定向地址(
https://<backend-host>/api/v1/sso/callback/<集成名称>,例如…/api/v1/sso/callback/feishu)加入应用的重定向 URL 白名单;该地址可直接一键复制。 - 为应用授予登录所需 scope:
contact:user.base:readonly contact:user.email:readonly。 - 在站点设置 → 集成 → 飞书卡片的能力区域打开飞书登录开关,并按需填写登录按钮文案。修改登录开关属于登录集成权限,飞书卡片上的其他设置属于集成设置权限,因此操作者通常需要同时具备两者。
- 如果尚未配置应用凭据,在同一张卡片上填写;凭据与通讯录同步共用,密钥留空表示保留原值。保持集成处于启用状态。
成员可以在个人设置 → 账户安全的关联账号区域点击绑定飞书,把飞书身份关联到已有账号;该入口同样要等管理员启用飞书用户登录后才出现。
只有在全局 SSO 已启用、集成已启用、且飞书登录开关打开时,登录页才会展示飞书按钮。未开启该选项时,飞书只提供通讯录同步。关闭集成会同时停用两项能力,但保留该选项。
登录身份以飞书资料中的 union_id 作为外部身份标识存储;缺少 union_id 时回退为 open_id,因此在同一开发者账号内更换应用不会导致已绑定账号失效。
通讯录中没有任何邮箱的飞书账号,会使用与目录同步一致的确定性占位邮箱,使登录与同步汇聚到同一个本地账号而不是各建一个。推导该地址需要飞书 user_id,因此需要 contact:user.employee_id:readonly;缺少该 scope 时这类账号的首次登录会被拒绝,而不会创建占位身份。
测试连接
集成卡片上的测试连接按钮(Test Connection)会在服务端仅本地构造一次授权地址(使用占位回调地址),检查配置里必需的键是否齐全:
- 成功只说明配置能构造出授权地址。
- 它不会访问身份提供商,因此客户端密钥错误、回调地址不匹配、证书过期这类问题它都测不出来——必须用真实登录验证。
通用流程
- 在站点设置 → 安全中启用 SSO,并按需调整全局策略(允许密码登录、自动创建、需要审批、按邮箱匹配)。
- 在站点设置 → 集成添加登录集成:填写基本信息,选择协议。
- 在 Configuration 页签填入该协议的 JSON 配置,把密钥填进 Credentials 而不是 Configuration。
- 在 Attribute Mapping 页签把邮箱、用户名、头像对应到提供商字段。
- 保存后,把回调地址(
https://your-domain.com/api/v1/sso/callback/<集成名称>)填入身份提供商的白名单/重定向地址。 - 用隐身窗口验证三种情况:新用户、已存在用户、错误回调。

用户生命周期
全局策略与集成自己的设置是或的关系,最终判定如下:
| 结果 | 判定规则 |
|---|---|
| 关联已有账户 | 全局开启「按邮箱匹配已有账户」(默认开启)且返回邮箱能匹配到现有用户;关联时同时确保该用户加入默认团队 |
| 新建账户 | 全局「自动创建用户」或集成自己的「允许注册」任一开启 |
| 新建被拒 | 全局自动创建关闭且集成允许注册也关闭 |
| 需要审批 | 全局「新账户需要审批」或集成的「需要审批」任一开启 → 账户为待审批且不可登录 |
| 仅 SSO 登录 | 全局关闭「允许密码登录」后,密码登录被拒绝 |
新建的 SSO 用户具有以下特征:
- 没有本地密码,因此无法用密码登录。
- 邮箱直接标记为已验证(信任提供商的邮箱)。
- 用户名取映射出的
username;缺失时用邮箱@前的部分,再缺失时用外部身份标识前 8 位加前缀;重名会自动追加数字后缀。 - 角色取集成设置的默认角色,未设置时回退到全局默认角色;随后按默认团队设置加入团队。
升级注意事项
从旧版 SSO 提供商模型升级到统一集成模型时:升级会保留集成标识、用户身份连接与外部身份 ID,无需重新绑定账号,也无需修改身份提供方的回调地址。
升级前备份数据库,并按升级文档要求的顺序更新全部组件;不支持新旧版本混用的滚动部署。回滚必须同时恢复切换前的数据库、匹配的应用版本与加密密钥。完整的升级与回滚步骤见升级 Clouisle。
故障排除
登录页没有 SSO 按钮
- 检查站点设置 → 安全中的全局 SSO 开关是否打开。
- 检查该集成是否启用,且飞书登录开关(如使用飞书)已打开。
- 确认身份提供商配置的回调地址与上面的格式一致。
回调报错 / 停在同一页面
- 核对回调地址的对外域名与集成名称是否与配置完全一致。
- 确认对外地址是身份提供商可达的公网地址。
- 检查用户是否在授权页停留过久(登录会话有效期 10 分钟,超时需重新发起)。
- 查看审计日志中的 SSO 登录失败记录与错误信息。
登录成功但字段错位
- 按属性映射确认路径写法(点号 vs
$JSONPath)。 - 注意同名原始字段会覆盖映射值。
- GitHub 等非 OIDC 标准字段需显式映射(
login、avatar_url)。
SAML 校验失败
- 证书必须是 PEM 内容且去掉头尾行。
- 确认 IdP 与服务器时间同步(偏差过大会被判定为无效响应)。
- 确认
idp_entity_id与 IdP 元数据中的 Entity ID 完全一致。
飞书回调被拒或未创建账号
- 确认应用已登记该集成的回调地址(以飞书卡片显示的地址为准)。
- 确认登录 scope 包含
contact:user.email:readonly;通讯录中没有邮箱的账号还需contact:user.employee_id:readonly,否则没有可据以推导占位邮箱的user_id,首次登录会被拒绝。 - 飞书部门浏览或同步提示集成未启用时:先保存 App ID 和 App Secret,再启用飞书集成并打开通讯录同步开关。
关闭密码登录后无人能进后台
这是把站点切成 SSO-only 的典型事故:请先确认至少一个超级管理员已绑定身份连接。若已经锁死,需要平台运维在服务端恢复密码登录开关。
相关文档
- SSO API — 登录流程、集成管理端点与错误码
- 登录与账户安全 — 登录保护与 SSO 首次登录行为
- 个人账户设置 — 解除关联与防自锁保护
- 站点设置参考 — SSO 与安全策略的默认值
- 升级 Clouisle — 从旧 SSO 模型迁移与回滚
这篇文章对你有帮助吗?