4.8 KiB
用甄养堂账号登录行知(AI 模块单点登录,2026-10-10)
行知(AI 工作助手)的登录页增加“使用甄养堂账号登录”:跳到甄养堂的登录确认页,确认后回到行知,行知首次登录自动开通成员账号,并同时完成“绑定甄养堂账号”(只读授权)。密码只在甄养堂输入和校验,不经过行知。
改动范围(都在 AI 模块 server/app/mcp 内,原有后台接口、Logic、中间件、配置未改)
| 文件 | 说明 |
|---|---|
controller/SsoController.php(新增) |
GET /mcp/sso/authorize 登录确认页;POST /mcp/sso/whois、/approve、/login 页面用;POST /mcp/sso/token 行知服务器换取结果 |
service/SsoService.php(新增) |
客户端与回调地址校验、浏览器里已登录后台会话的识别、一次性授权码(2 分钟、只能用一次,文件锁防并发兑换) |
service/GrantService.php(AI 模块自身) |
issue() 拆成 verifyPassword() + issueFor(),assertAdminUsable() 改为 public;绑定接口 /mcp/auth/grant 的行为不变 |
service/McpConfig.php(AI 模块自身) |
新增 ssoEnabled / ssoClientId / ssoClientSecret / ssoRedirectUris |
server/tests/AiMcpSsoTest.php(新增) |
见“测试” |
不需要执行新的数据库迁移;不新增权限点(沿用 ai.mcp/access)。
流程与门禁
- 行知把浏览器带到
/mcp/sso/authorize?client_id&redirect_uri&state。client_id、redirect_uri必须与 .env 登记的完全一致,否则只显示原因、不显示登录框、不跳转。 - 确认身份,两种方式:
- 浏览器里已登录甄养堂后台(localStorage
like_admin_token):显示“你已登录甄养堂后台:某某”,点“以此账号登录”。只认电脑端、手机端的正常登录(terminal 1/2),AI 后台浏览器(8)、企微客服端(7/9)和过期会话一律不认。 - 输入账号密码:与“绑定甄养堂账号”同一套校验(按 IP 限流、连续错误锁定、停用、企微强制绑定、
ai.mcp/access权限点、未改初始密码)。
- 浏览器里已登录甄养堂后台(localStorage
- 带一次性授权码回到行知;行知服务器用客户端密钥调用
/mcp/sso/token(经过ALLOWED_IPS/ Origin 检查),得到账号信息和只读令牌(与绑定签发的授权相同,同一行知实例的旧授权自动作废)。 - 页面:严格 CSP(脚本只认本页 nonce、
frame-ancestors 'none'、form-action 'none')、X-Frame-Options: DENY、Cache-Control: no-store、Referrer-Policy: no-referrer,并覆盖全局中间件的Access-Control-Allow-Origin: *。 - 审计:
zyt_ai_access_log的sso.approve、sso.login(拒绝时)、sso.token。
上线步骤
-
同步上面四个文件到生产(
SsoController.php、SsoService.php、GrantService.php、McpConfig.php)。 -
在行知管理端 → 组织连接器 → 甄养堂业务系统 → “账号登录行知” → “生成密钥”,把显示的四行加到生产
.env的[AI_MCP]段(与ENABLED = true同一段),例如:SSO_ENABLED = true SSO_CLIENT_ID = xingzhi SSO_CLIENT_SECRET = (行知管理端生成,只显示一次) SSO_REDIRECT_URIS = http://xz.zhenyangtang.cn:8787/api/auth/sso/zyt/callback -
重新加载 PHP(PHP-FPM / 宝塔里重载或重启 PHP),然后在行知管理端勾选“开启:登录页显示这个按钮”并保存。
-
如设置了
ALLOWED_IPS,需包含行知服务器的出口 IP(/mcp/sso/token由行知服务器调用);登录确认页本身面向员工浏览器,不受这项限制。 -
停用:把
SSO_ENABLED改为 false(或删掉这几行)并重载 PHP;行知侧也可在管理端取消勾选。
测试
source ~/.cache/zyt-tools/scripts/zyt_env.sh # 本地 PHP 8.2 + MariaDB(zyt_mcp_test)
export PHP_AI_MCP_SSO_ENABLED=true PHP_AI_MCP_SSO_CLIENT_ID=xingzhi
export PHP_AI_MCP_SSO_CLIENT_SECRET=<至少 32 位> PHP_AI_MCP_SSO_REDIRECT_URIS=http://127.0.0.1:18787/api/auth/sso/zyt/callback
# 服务端与测试进程使用同一组 SSO 配置
AI_MCP_TEST_MYSQL=1 AI_MCP_TEST_BASE_URL=http://127.0.0.1:8099 $PHP server/tests/AiMcpSsoTest.php
覆盖:确认页只对登记的客户端显示(安全响应头、未登记地址不回显)、账号密码确认的各项门禁、一键确认只认电脑端/手机端会话、授权码要客户端密钥并与回调地址一致且只能用一次、确认后被停用的账号兑换失败、换来的令牌可直接使用并替换同一实例的旧授权、审计、原有绑定接口不受影响。原有 AiMcpUnitTest、AiMcpHttpContractTest、AiMcpConsoleTest、AiMcpPerfTest 回归通过。与行知的端到端联调(真实 zyt 代码 + 行知登录页,账号密码与一键两种方式)也已通过。