From fc7f3d7255b4210103f5501726c97a73b4d8e937 Mon Sep 17 00:00:00 2001 From: gitadmin Date: Tue, 30 Jun 2026 17:09:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20SSO=20=E7=BB=9F=E4=B8=80=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 集成 Authelia OIDC,支持统一认证登录 - 添加 OIDC 登录页面和回调处理 - 更新 docker-compose.yml 统一环境变量管理 - 更新 CHANGELOG.md 和 CLAUDE.md --- .env.example | 14 ++++++++++++++ CHANGELOG.md | 14 ++++++++++++++ CLAUDE.md | 9 +++++++++ docker-compose.yml | 4 ++++ src/lib/oidc.ts | 1 + 5 files changed, 42 insertions(+) diff --git a/.env.example b/.env.example index 057b9ba..9607ee0 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,23 @@ DATABASE_PATH=./data/assets.db JWT_SECRET=your-secret-key-change-in-production NODE_ENV=development +# ⚠️ 仅限本地开发环境(自签名证书),生产环境禁止设置此变量 +NODE_TLS_REJECT_UNAUTHORIZED=0 + +# LDAP 配置 +LDAP_URL=ldap://localhost:3890 +LDAP_BASE_DN=dc=tlyq,dc=ai +LDAP_ADMIN_DN=uid=admin,ou=people,dc=tlyq,dc=ai # issue-ai API 配置(用于故障历史功能) # NEXT_PUBLIC_ 前缀:构建时内嵌到客户端 JS,云上必须通过 deploy-ai.sh 设置 # 本地开发:http://localhost:6176/tickets # 云上生产:https://issue.tlyq.ai/tickets NEXT_PUBLIC_ISSUE_URL=http://localhost:6176/tickets +ISSUE_API_URL=http://localhost:6176/api + +# OIDC 配置(SSO 统一认证) +AUTHELIA_URL=https://sso.tlyq.ai +OIDC_CLIENT_ID=assets-oidc +OIDC_CLIENT_SECRET=change-me-to-hashed-secret +OIDC_REDIRECT_URI=http://localhost:6177/api/auth/callback diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d1d645..2fa159d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # 变更日志 +## 2026-06-30 + +- [新增] SSO 统一认证:集成 Authelia OIDC,支持统一认证登录 +- [新增] OIDC 登录页面:添加「统一认证登录」按钮,支持 LDAP 回退 +- [新增] `src/lib/oidc.ts`:OIDC 客户端配置(PKCE + state + nonce) +- [新增] `src/app/api/auth/login/oidc/route.ts`:OIDC 登录端点 +- [新增] `src/app/api/auth/callback/route.ts`:OIDC 回调处理(含用户自动创建) +- [新增] `src/app/api/auth/logout/route.ts`:跨域登出(支持 domain 参数) +- [修复] NODE_TLS_REJECT_UNAUTHORIZED=0:Authelia 使用自签名证书 +- [修复] OIDC redirect_uri 回调重定向到 localhost:添加 getBaseUrl() 函数 +- [修复] token_endpoint_auth_method 配置缺失:添加 client_secret_basic +- [优化] docker-compose.yml:统一环境变量管理,移除 env_file +- [优化] .env.example:添加 OIDC 配置模板 + ## 2026-06-24 - [修复] SQLite 时区根源修复:重建表修改所有时间列 DEFAULT 为 `datetime('now', '+8 hours')`,涉及 6 个表 8 个列 diff --git a/CLAUDE.md b/CLAUDE.md index caa06cb..921c299 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -154,6 +154,11 @@ npm run import # 导入设备数据 | `JWT_SECRET` | `dev-secret-key-local` | `${ASSETS_JWT_SECRET}` | 生产必须强密钥 | | `DATABASE_PATH` | `./data/assets.db` | `/app/data/assets.db` | Docker volume 挂载 | | Cookie 名 | `session_assets` | `session_assets` | 本地两系统用不同名防 localhost 域冲突 | +| `AUTHELIA_URL` | `https://sso.tlyq.ai` | 同 | Authelia 地址 | +| `OIDC_CLIENT_ID` | `assets-oidc` | 同 | OIDC 客户端 ID | +| `OIDC_CLIENT_SECRET` | 本地生成的哈希值 | 服务器生成的哈希值 | OIDC 客户端密钥 | +| `OIDC_REDIRECT_URI` | `http://localhost:6177/api/auth/callback` | `https://assets.tlyq.ai/api/auth/callback` | OIDC 回调地址 | +| `NODE_TLS_REJECT_UNAUTHORIZED` | 不需要 | `0`(Authelia 使用自签名证书) | TLS 证书验证 | ### `.env.local` 示例 @@ -164,6 +169,10 @@ NODE_ENV=development ISSUE_API_URL=http://localhost:6176/api ISSUE_API_KEY=ak_<32字节十六进制> NEXT_PUBLIC_ISSUE_URL=http://localhost:6176/tickets +AUTHELIA_URL=https://sso.tlyq.ai +OIDC_CLIENT_ID=assets-oidc +OIDC_CLIENT_SECRET=<本地生成的哈希值> +OIDC_REDIRECT_URI=http://localhost:6177/api/auth/callback ``` --- diff --git a/docker-compose.yml b/docker-compose.yml index ba843dd..ce2f5d2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,6 +30,10 @@ services: - ALLOWED_API_KEYS=${ALLOWED_API_KEYS} # 故障历史跳转的工单系统地址(客户端使用) - NEXT_PUBLIC_ISSUE_URL=https://issue.tlyq.ai/tickets + # OIDC 配置 + - OIDC_CLIENT_ID=${OIDC_CLIENT_ID:-assets-oidc} + - OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET} + - OIDC_REDIRECT_URI=${OIDC_REDIRECT_URI:-https://assets.tlyq.ai/api/auth/callback} restart: unless-stopped networks: - webnet diff --git a/src/lib/oidc.ts b/src/lib/oidc.ts index f0390a7..988290b 100644 --- a/src/lib/oidc.ts +++ b/src/lib/oidc.ts @@ -23,6 +23,7 @@ export async function getOidcClient() { client_secret: OIDC_CLIENT_SECRET, redirect_uris: [OIDC_REDIRECT_URI], response_types: ['code'], + token_endpoint_auth_method: 'client_secret_basic', }) lastDiscovery = now return oidcClient