# DeepData × Casdoor SSO 部署运维手册(31.23) > 适用范围:DeepData(`com.deepdata`)网关单点登录接入自建 Casdoor v2.153.0。 > 生产实例:31.23(Casdoor `:1013`,DeepData 网关 `:8080`,前端/入口 `deepdata.yhdd.apescale.com:1014`)。 > 代码版本:≥ `4e2ec55`(M3:移除 accountOnly,本地登录与 SSO 共存;登录时自动建号,无定时同步)。 ## 1. 架构与登录流 ``` 浏览器 → DeepData 网关 /api/user/oauth/login?next=… → 302 Casdoor /login/oauth/authorize(state+next 存 HttpOnly cookie,TTL 300s) → Casdoor 登录/同意 → 回调 /api/user/oauth/callback?code&state → 网关换 token → JWKS 验 id_token → upsert 本地用户(自动建号/刷组刷角色) → 302 next?sso_token=<本地HS256 JWT>(含 role/groups claims) → 前端 callback.html 种 token 进入应用 ``` 失败时回跳 `next?sso_error=`(`state_mismatch` / `code_exchange` / `id_token_verify` / `internal`)。 角色映射:Casdoor 组 `deepdata-admin|dev|annotator|viewer`(组路径 `datamate/`)→ 本地 `role`,由 `RoleResolver` 按「映射 > 主角色 > defaultRole」解析;组与角色随每次 SSO 登录刷新。 ## 2. Casdoor 侧一次性配置(生产现状) | 对象 | 值 | 说明 | |------|-----|------| | 组织 | `admin/built-in`、`admin/datamate` | `password_type=bcrypt` | | 组(owner=datamate) | `deepdata-admin` `deepdata-dev` `deepdata-annotator` `deepdata-viewer` | 与 casbin 规则对齐 | | 用户(owner=datamate) | `dmin`(4 组)、`viewer1`(viewer) | **`tag='DeepData'` 必须与应用 tags 一致**;建议给真实 email | | 证书 | `admin/cert-built-in` | 应用引用用**裸名** `cert-built-in` | | 应用 | `admin/DataMate` | clientId `144f4d00553aa7db0aab`;tags `["DeepData"]`;tokenFields `["Groups"]`;`redirectUri=https://deepdata.yhdd.apescale.com:1014/api/user/oauth/callback`;**`expire_in_hours ≥ 1`** | | Casbin | dmin 4 条 / viewer1 1 条 | 角色→组权限 | ### 踩坑清单(均已踩过,排查先对这里) 1. **`expire_in_hours` 必须 ≥ 1**:应用列名是 `expire_in_hours`(double),不是 token_expiration。为 0 时 `expires_in=0`,id_token `exp=iat`,网关验证报 `JWT expired N milliseconds ago`。 `UPDATE application SET expire_in_hours=1 WHERE owner='admin' AND name='DataMate';` 2. **用户 tag 必须在应用 tags 里**:否则 casdoor 登录报 `User's tag: is not listed in the application's tags`。 3. **`/api/login` 收 JSON body**(不是表单):`{"type":"login","application":"DataMate","username":"dmin","password":"…","organization":"datamate","autoSignin":true}`。 4. **`/api/grant-consent` 的 `application` 必须全名** `admin/DataMate`(裸名报 `Invalid application`);session cookie 与登录**同 host**(cookie 域陷阱)。 5. **authorize 无服务端 302**:带 session 的 GET 只返回 SPA 页面,重定向由前端 `grant-consent` 后完成——脚本化验证直接调 `POST /api/grant-consent`。 ## 3. DeepData 侧配置 `deployment/docker/deepdata/.env`(compose 注入,变量名映射见 `docker-compose.yml`): ```ini DEEPDATA_CASDOOR_ENABLED=true DEEPDATA_CASDOOR_ISSUER=https://sso.yhdd.apescale.com:1014 DEEPDATA_CASDOOR_AUTHORIZE_URI=http://192.168.31.23:1013/login/oauth/authorize DEEPDATA_CASDOOR_TOKEN_URI=http://192.168.31.23:1013/api/login/oauth/access_token DEEPDATA_CASDOOR_JWKS_URI=http://192.168.31.23:1013/.well-known/jwks.json DEEPDATA_CASDOOR_CLIENT_ID=144f4d00553aa7db0aab DEEPDATA_CASDOOR_CLIENT_SECRET=*** DEEPDATA_CASDOOR_REDIRECT_URI=https://deepdata.yhdd.apescale.com:1014/api/user/oauth/callback JWT_SECRET=<不变,本地 JWT 签名密钥> ``` > ISSUER 用公网域名(id_token issuer 断言用);三个 URI 用内网直连(网关→casdoor 同机)。 ## 4. 构建与部署 ```bash cd /opt/deepdata/deepdata make gateway-docker-build frontend-docker-build # 产物 deepdata-{gateway,frontend}:latest cd deployment/docker/deepdata docker compose up -d --force-recreate deepdata-gateway deepdata-frontend ``` 注意:跳板机链路构建长任务易断连,长构建用 `nohup … > /tmp/gw_build.log 2>&1 &` 后台跑再轮询日志。 同步代码到 31.23 **只按文件同步、禁用 `rsync --delete`**,同步后 `git hash-object` 逐文件核对。 ## 5. 验证 ```bash GW_BASE=https://deepdata.yhdd.apescale.com:1014 \ CASDOOR_BASE=http://192.168.31.23:1013 \ SSO_CLIENT_ID=144f4d00553aa7db0aab \ LOCAL_ADMIN_PASSWORD=<本地admin密码> \ SSO_TEST_USERS="dmin::admin viewer1::viewer" \ bash scripts/casdoor-sso/e2e-check.sh ``` 覆盖:配置公开且无 secret 泄漏 → 本地登录(与 SSO 共存)→ SSO 全链路(authorize→登录→consent→callback→sso_token→/me 角色断言)。 网络注意:公网域名经 222.128.20.101 转发到 31.23:1014,**不要用 `--resolve` 把域名指到 192.168.31.23**(该地址本地未监听);E2E 直接访问公网域名即可。 ## 6. 排障 | 现象 | 原因 | 处理 | |------|------|------| | `sso_error=id_token_verify` + 网关日志 `JWT expired` | 应用 `expire_in_hours=0` | 见 §2 坑 1 | | `sso_error=internal` + `provision race … reusing existing user` 后 NPE | 本地 email 列 NOT NULL UNIQUE,空 email 互撞(代码已修为空 email 合成 `username@casdoor.local`) | 升级网关镜像;存量行 `UPDATE users SET email=… WHERE email='';` | | casdoor 登录 `User's tag: is not listed…` | 用户 tag 不在应用 tags | `UPDATE user SET tag='DeepData' WHERE owner='datamate';` | | grant-consent `Invalid application` | application 用了裸名 | 用 `admin/DataMate` | | authorize 返回 200 不跳转 | 正常(SPA 行为);若脚本验证用 grant-consent | — | | `/sso/casdoor/config` `enabled:false` | `DEEPDATA_CASDOOR_ENABLED` 未注入 | 检查 .env + compose 变量名映射 | | `/sso/callback.html` 403(nginx permission denied) | 文件权限 600,nginx worker 非 root 读不到(构建把 600 烤进镜像) | `chmod 644 frontend/public/sso/callback.html` 后重建前端镜像 |