mirror of
https://github.com/iflytek/skillhub.git
synced 2026-10-07 02:57:51 +00:00
179 lines
7.8 KiB
Markdown
179 lines
7.8 KiB
Markdown
# CAS 2.0/3.0 接入指南
|
||
|
||
本文说明如何把一个 CAS Provider Instance 接入 SkillHub 统一身份核心。CAS 登录默认关闭,
|
||
配置不完整或 Authority 状态异常时不会出现在登录方法目录中。
|
||
|
||
## 1. 前置条件
|
||
|
||
- SkillHub 的公开入口和 CAS Server 使用 HTTPS。
|
||
- Redis 可用。CAS state 使用 Redis 原子消费,以支持多 Pod 和重放保护。
|
||
- CAS Server 已允许下面的精确 Service URL:
|
||
|
||
```text
|
||
https://<skillhub-host>/api/v1/auth/cas/<provider-code>/callback
|
||
```
|
||
|
||
- 已确认可作为稳定身份键的 CAS principal 或 immutable attribute。
|
||
|
||
`provider-code` 和 `authority` 是持久身份绑定的一部分。产生 Identity Binding 后不能通过
|
||
普通配置变更切换到另一个身份域。
|
||
|
||
## 2. 配置
|
||
|
||
| 环境变量 | 必填 | 默认值 | 说明 |
|
||
|---|---:|---|---|
|
||
| `SKILLHUB_AUTH_CAS_ENABLED` | 是 | `false` | 显式启用 CAS |
|
||
| `SKILLHUB_AUTH_CAS_PROVIDER_CODE` | 是 | `cas` | Provider Instance code,需长期稳定 |
|
||
| `SKILLHUB_AUTH_CAS_DISPLAY_NAME` | 是 | `CAS` | 登录页展示名 |
|
||
| `SKILLHUB_AUTH_CAS_AUTHORITY` | 是 | 空 | 稳定 CAS 身份域 ID |
|
||
| `SKILLHUB_AUTH_CAS_SERVER_URL` | 是 | 空 | CAS Server 根地址,例如 `https://cas.example.com/cas` |
|
||
| `SKILLHUB_AUTH_CAS_SERVICE_URL` | 是 | 空 | 精确 SkillHub callback,不含 query/fragment |
|
||
| `SKILLHUB_AUTH_CAS_PROTOCOL_VERSION` | 是 | `3.0` | `2.0` 或 `3.0` |
|
||
| `SKILLHUB_AUTH_CAS_SUBJECT_TYPE` | 是 | `cas_principal` | 统一身份核心中的 Subject type |
|
||
| `SKILLHUB_AUTH_CAS_ATTRIBUTES_SUBJECT` | 否 | 空 | 为空时使用 CAS principal;否则使用唯一 immutable attribute |
|
||
| `SKILLHUB_AUTH_CAS_ATTRIBUTES_DISPLAY_NAME` | 否 | 空 | display name attribute |
|
||
| `SKILLHUB_AUTH_CAS_ATTRIBUTES_EMAIL` | 否 | 空 | email attribute,固定按 asserted 处理 |
|
||
| `SKILLHUB_AUTH_CAS_ATTRIBUTES_AVATAR_URL` | 否 | 空 | avatar URL attribute |
|
||
| `SKILLHUB_AUTH_CAS_CONNECT_TIMEOUT` | 否 | `PT5S` | 连接超时 |
|
||
| `SKILLHUB_AUTH_CAS_READ_TIMEOUT` | 否 | `PT10S` | 请求总超时 |
|
||
| `SKILLHUB_AUTH_CAS_STATE_TTL` | 否 | `PT5M` | 登录 state 有效期,最大 15 分钟 |
|
||
| `SKILLHUB_AUTH_CAS_MAX_RESPONSE_BYTES` | 否 | `1048576` | validation 响应上限,1 KiB–1 MiB |
|
||
|
||
CAS 返回的普通 email attribute 不是 verified email,不能用于
|
||
`EMAIL_DOMAIN` 准入或静默账号绑定。
|
||
|
||
### Docker Compose
|
||
|
||
编辑 `.env.release`:
|
||
|
||
```dotenv
|
||
SKILLHUB_AUTH_CAS_ENABLED=true
|
||
SKILLHUB_AUTH_CAS_PROVIDER_CODE=cas-main
|
||
SKILLHUB_AUTH_CAS_DISPLAY_NAME=Corporate CAS
|
||
SKILLHUB_AUTH_CAS_AUTHORITY=corp-cas
|
||
SKILLHUB_AUTH_CAS_SERVER_URL=https://cas.example.com/cas
|
||
SKILLHUB_AUTH_CAS_SERVICE_URL=https://skills.example.com/api/v1/auth/cas/cas-main/callback
|
||
SKILLHUB_AUTH_CAS_PROTOCOL_VERSION=3.0
|
||
SKILLHUB_AUTH_CAS_SUBJECT_TYPE=cas_principal
|
||
SKILLHUB_AUTH_CAS_ATTRIBUTES_DISPLAY_NAME=displayName
|
||
SKILLHUB_AUTH_CAS_ATTRIBUTES_EMAIL=mail
|
||
```
|
||
|
||
启动前运行:
|
||
|
||
```bash
|
||
./scripts/validate-release-config.sh .env.release
|
||
```
|
||
|
||
### Helm
|
||
|
||
```yaml
|
||
auth:
|
||
cas:
|
||
enabled: true
|
||
providerCode: cas-main
|
||
displayName: Corporate CAS
|
||
authority: corp-cas
|
||
serverUrl: https://cas.example.com/cas
|
||
serviceUrl: https://skills.example.com/api/v1/auth/cas/cas-main/callback
|
||
protocolVersion: "3.0"
|
||
subjectType: cas_principal
|
||
connectTimeout: PT5S
|
||
readTimeout: PT10S
|
||
stateTtl: PT5M
|
||
maxResponseBytes: 1048576
|
||
attributes:
|
||
subject: ""
|
||
displayName: displayName
|
||
email: mail
|
||
avatarUrl: ""
|
||
```
|
||
|
||
Chart 在渲染阶段拒绝 HTTP endpoint、缺失 URL 和 provider code 不匹配的 callback。
|
||
|
||
### Kustomize
|
||
|
||
修改 `deploy/k8s/base/configmap.yaml` 中的 `auth-cas-*` 字段,然后重新应用 overlay。
|
||
CAS 配置不包含 client secret;若企业扩展引入凭证,必须放入 Kubernetes Secret,不能放
|
||
在 ConfigMap。
|
||
|
||
## 3. 身份映射
|
||
|
||
默认映射:
|
||
|
||
```text
|
||
primary subject = CAS principal
|
||
subject type = cas_principal
|
||
email assurance = PROVIDER_ASSERTED
|
||
aliases = none
|
||
```
|
||
|
||
如果 CAS principal 可能随用户名变更,必须配置
|
||
`SKILLHUB_AUTH_CAS_ATTRIBUTES_SUBJECT` 指向 CAS Server 明确定义且实际返回的 immutable
|
||
attribute,并为该属性选择稳定的 `subject-type`。属性缺失、多值或空值时登录 fail closed。
|
||
|
||
不要把 email、display name 或临时用户名当作稳定 Subject。
|
||
|
||
## 4. 验证
|
||
|
||
1. 匿名请求登录方法目录:
|
||
|
||
```bash
|
||
curl -fsS 'https://skills.example.com/api/v1/auth/methods'
|
||
```
|
||
|
||
只在 Provider 为 `READY` 时应出现 `CAS_REDIRECT`。
|
||
|
||
2. 在浏览器点击 CAS 登录,确认重定向到 CAS `/login`,其 `service` 解码后与配置的
|
||
callback 加一次性 `state` 完全一致。
|
||
3. 完成 CAS 登录,确认返回原始 `returnTo` 或默认页面,并能请求
|
||
`/api/v1/auth/me`。
|
||
4. 再次请求相同 callback,必须得到 `casInvalidState` 或 CAS Ticket 验证失败,不能建立
|
||
第二个 Session。
|
||
5. 检查应用日志中不包含 `ST-` Ticket、validation URL、上游完整响应或用户属性。
|
||
6. 禁用 Provider 后,方法目录不再显示 CAS,且点击旧 URL 不应连接 CAS Server。
|
||
7. 在账号安全页验证 CAS Identity Link:已绑定 CAS 可以完成 fresh reauthentication,
|
||
READY intent 可以通过目标 CAS 创建 Binding;失败后应回到同一 intent,成功后不能重放。
|
||
|
||
建议分别验证 CAS 2 XML、CAS 3 JSON、CAS 3 XML fallback、无效 Ticket、错误 Service、
|
||
超时、TLS 失败、XXE、超大响应和 Redis 不可用。
|
||
|
||
CAS callback 按协议会在 query string 中携带一次性 `ticket` 和 `state`。官方 Web
|
||
镜像不记录 query string 或 Referer,应用请求日志会对它们进行隐藏;如果前面还有
|
||
Ingress、负载均衡、WAF、APM 或其他反向代理,必须将该 callback 路径的 query string
|
||
和 Referer 关闭记录或至少对 `ticket`、`state` 脱敏。上线前应使用唯一测试 Ticket
|
||
检查完整日志链路,确认没有原文残留。重复 state 会分类为 `REPLAY_DETECTED` 并写入
|
||
不含 state/Ticket 的安全审计。
|
||
|
||
## 5. 升级与回滚
|
||
|
||
- 新配置默认关闭,不影响现有本地密码、GitHub、GitLab 或 OIDC 登录。
|
||
- 本次接入不修改 `PlatformPrincipal` 或现有 Session 序列化结构。
|
||
- 旧版本回滚后会忽略 CAS 环境变量;现有非 CAS 登录仍可使用。
|
||
- CAS 已产生 Binding 后回滚会暂时失去 CAS 登录入口,但不会删除账号、Binding 或业务
|
||
数据。
|
||
- 修改 CAS endpoint 可以保持同一 Authority;修改 Authority 必须走统一身份设计中的
|
||
显式 Authority 迁移流程。
|
||
|
||
## 6. 安全实现说明
|
||
|
||
实现阶段验证了 Spring Security CAS 所依赖的 Apereo Java CAS Client。其 4.1.1
|
||
`AbstractUrlBasedTicketValidator` 仍会在 DEBUG 记录包含 Ticket 的 validation URL 和完整
|
||
响应;CAS 2 自定义属性解析路径也没有满足本项目要求的全部 XML parser fail-closed
|
||
设置。因此当前实现没有调用该库的 URL validator,而是使用受限 JDK HTTP transport 和
|
||
严格 parser:
|
||
|
||
- 禁止 redirect;
|
||
- 明确连接、请求和响应大小上限;
|
||
- XML 禁止 DOCTYPE、外部实体、外部 DTD 和外部 schema;
|
||
- Ticket、完整响应和属性不写日志或异常;
|
||
- CAS 3 优先 JSON,并支持受限 XML fallback。
|
||
|
||
上游参考:
|
||
|
||
- [Spring Security CAS reference](https://docs.spring.io/spring-security/reference/servlet/authentication/cas.html)
|
||
- [Apereo validator logging](https://github.com/apereo/java-cas-client/blob/cas-client-4.1.1/cas-client-core/src/main/java/org/apereo/cas/client/validation/AbstractUrlBasedTicketValidator.java)
|
||
- [Apereo CAS 2 attribute parser](https://github.com/apereo/java-cas-client/blob/cas-client-4.1.1/cas-client-core/src/main/java/org/apereo/cas/client/validation/Cas20ServiceTicketValidator.java)
|
||
|
||
如果上游版本修复这些问题,可以在保持错误分类、响应上限和无敏感日志测试的前提下重新
|
||
评估替换 transport/parser。
|