工单中心 × OAuth2.0 认证中心对接计划
2026-10-15 认证中心重构说明:oauth 已从 plugin/oauth 插件形态重构为自有 webman 项目(app/ 下 admin/index/store 三应用,config/route.php 集中路由)。端点为无前缀:POST /token、GET /userinfo、GET /user-permissions、GET /login;平台管理后台入口 http://127.0.0.1:8788/admin(RBAC 权限管理:/admin/api/clients、/admin/api/permissions、/admin/api/roles、/admin/api/user-roles/set)。工单中心已按此适配(config/app.php 的 oauth.base_url + OAuthClient 路径)。
woc 应用:client_id woc,secret 79220b54d91d7753af40f2de4f7cbf289ab2c49375083a3a(grant_types: password/refresh_token/client_credentials)。
目标:工单中心后台(/console、/api/*)当前无登录与权限控制,需统一走公司 OAuth2.0 认证中心(/Users/body/ai_code/oauth)登录,并按角色做权限判断。
一、OAuth 认证中心能力分析(调研结论)
1.1 部署形态
| 项 |
结论 |
证据 |
| 服务 |
独立 webman 项目,默认监听 8788,可 OAUTH_LISTEN_PORT 覆盖 |
config/process.php、README |
| 插件形态 |
自有 webman 项目(app/ 下 admin/index/store 三应用 + config/route.php 集中路由);作为独立服务被 HTTP 调用 |
composer.json、plugin/oauth/app/service/Database.php |
| 数据库 |
SQLite 默认(runtime/oauth.db)/MySQL 可切换;模型固定使用名为 oauth 的连接 |
config/database.php、Database.php:16-40 |
| 用户表 |
user_provider.table 默认指向宿主 users 表(复用不覆盖,幂等补列) |
config/oauth.php |
1.2 登录与令牌
| 能力 |
说明 |
| 登录页 |
GET/POST /login(username/password/_csrf_token),支持 ?redirect= 站内回跳,登录成功重建 session ID |
| 授权模式 |
授权码、密码(password)、客户端凭证(client_credentials)、刷新(refresh_token)、权限刷新(refresh_permissions)、权限验证(access_permissions) |
| 令牌端点 |
POST /token;client 认证支持表单参数或 HTTP Basic |
| 令牌 TTL |
access_token 7200s / refresh_token 2592000s / auth_code 600s |
| 用户信息 |
GET /userinfo(Bearer)、GET /user-permissions(Bearer,含角色+权限码+权限树) |
| 远程权限校验 |
grant_type=access_permissions:传 client_id+client_secret+access_token+client_permission → 返回 {allowed: true/false} |
| 登录防爆破 |
LoginGuard:连续失败 5 次锁 15 分钟(按 IP/账号) |
1.3 用户/角色/权限模型
| 表 |
关键字段 |
说明 |
users(宿主) |
id, username, nickname, email, mobile, password(bcrypt), store_id, enabled |
资源所有者(前台登录用户) |
oauth_clients |
client_id(唯一), client_secret, redirect_uris, scopes, grant_types, max_tokens_per_user |
应用(每个业务系统一个) |
oauth_permissions |
client_id, parent_id, code(权限码), type(dir/menu/action) |
权限节点树,按 client 隔离 |
oauth_roles |
client_id, code, parent_id |
角色,按 client 隔离,子角色继承父角色 |
oauth_user_roles |
user_id, role_id |
用户-角色关联 |
oauth_role_permissions |
role_id, permission_id |
角色-权限关联 |
1.4 权限判断机制
- 前台用户接口权限:
OAuthAuthenticate(Bearer 校验)+ PermissionCheck('code') 中间件 → RbacService::userHasPermission(),链为 user_roles → roles → role_permissions → permissions
- 远程校验:
access_permissions grant(业务系统服务端带自己的 client 凭据 + 用户 token + 权限码远程问询)
- 测试账号:资源所有者
demo/123456;平台管理员 admin/admin123;种子客户端 zhongtai/zhongtai-secret
二、对接方案选型
2.1 集成方式:独立服务 HTTP 调用(推荐) vs 插件拷贝
| 维度 |
A. 独立服务 HTTP 调用 |
B. 插件拷贝进 lb_tips |
| 认证中心单一实例 |
✅ 全公司共用一台 |
❌ 每项目一份,数据分裂 |
| 代码维护 |
✅ 只维护 oauth 一处 |
❌ 两处同步升级 |
| 实现成本 |
中(写 OAuthClient 服务 + 中间件) |
低(直接复用中间件) |
| 端口/部署耦合 |
低(仅配置 base_url) |
高(lb_tips 也开 8788) |
决策:方案 A(独立服务 HTTP 调用)。oauth 是公司级认证中心,应保持单一实例;工单中心仅作为它的一个 client(client_id=woc)接入,符合其「第三方接入文档」标准做法。
2.2 登录方式:授权码模式 vs 密码模式
| 维度 |
授权码模式 |
密码模式(password grant) |
| 登录页归属 |
oauth 的 /login(样式不可定制为 TicketHub 风格) |
工单中心自建 TicketHub 风格登录页 |
| 安全 |
最高(标准 OAuth 网页流程) |
受信任内部客户端可接受;client_secret 存服务端 |
| 工单中心登录页 |
仅「跳转 oauth 登录」入口页 |
完整自建表单(用户要求"新增登录页") |
| 复杂度 |
需回调 + state 校验 |
仅调 token 端点 |
决策:密码模式 + 自建登录页。用户明确要求"新增一个登录页",且工单中心是内部系统(受信任客户端),password grant 满足需求且体验统一。client_secret 仅存工单中心服务端配置,不暴露前端。
2.3 权限判断:oauth RBAC 权限码 vs 本地角色
| 维度 |
oauth RBAC 权限码 |
本地角色(users.role) |
| 权限粒度 |
接口级权限码(workorder:create 等) |
角色级(cs/ops/admin)粗粒度 |
| 配置位置 |
oauth 后台(每 client 独立权限树+角色) |
工单中心 users 表 |
| 实现成本 |
高(需建权限节点树、角色、用户分配) |
低(已存在 role 字段) |
| 可扩展 |
强 |
弱 |
决策:一期本地角色(users.role),二期接入 oauth RBAC 权限码。一期快速闭环:登录时从 oauth 拿用户身份,工单中心本地 users.role 判断权限;二期在 oauth 后台为 woc 配置权限节点树+角色,改为 access_permissions 远程校验。
三、总体架构
┌─────────────────────────────┐ ┌──────────────────────────────┐
│ 工单中心 (lb_tips, 8787) │ HTTP │ OAuth2.0 认证中心 (8788) │
│ │ ────▶ │ POST /token (password) │
│ /login 自建登录页 │ │ GET /userinfo │
│ ↓ 用户名/密码 │ ◀──── │ (校验成功后) │
│ AuthController │ │ 用户表 users (username/ │
│ ↓ 换 access_token │ │ password/enabled) │
│ ↓ 取 userinfo (user_id) │ └──────────────────────────────┘
│ ↓ 查本地 users.role │
│ Session 写入登录态 │
│ ↓ │
│ AuthMiddleware 校验 Session │
│ /console + /api/* 权限判断 │
└─────────────────────────────┘
四、数据模型调整(工单中心侧)
4.1 users 表补列(tools/migrate.php)
工单中心 users 表补充字段,与 oauth 用户建立关联:
| 新字段 |
类型 |
说明 |
oauth_user_id |
bigint nullable 唯一 |
oauth 认证中心返回的用户主键 |
username |
varchar(100) nullable 唯一 |
oauth 登录用户名(便于运维对应) |
password |
varchar(255) nullable |
密码哈希(password_hash/bcrypt,与 oauth 同算法) |
说明:同一账号在两处各自建号(oauth 管身份、工单中心管业务档案),通过 oauth_user_id 关联;密码仅用于本地直连 SQLite 开发环境校验(可选),生产统一走 oauth 校验。
4.2 新增表:oauth_clients_meta(可选)
记录工单中心已注册的 oauth client 信息(client_id/client_secret/base_url),或直接用环境变量/config/app.php 配置,不建表。
五、实现步骤
M1:登录闭环(Session 会话 + 本地角色权限)
| # |
内容 |
文件 |
| 1 |
新增 OAuthClient 服务:封装 tokenPassword()、userinfo()、refreshToken()、accessPermissionCheck() |
app/service/OAuthClient.php |
| 2 |
新增 AuthController:GET /login(渲染登录页)、POST /login(调 oauth password grant → userinfo → 查本地 users.role → 写 Session)、GET /logout |
app/controller/AuthController.php |
| 3 |
新增 AuthMiddleware:校验 Session 登录态,未登录跳 /login;从 Session 读 role 挂到 request->user |
app/middleware/AuthMiddleware.php |
| 4 |
新增登录页 login.html(TicketHub 设计风格,含用户名/密码/错误提示) |
public/login.html |
| 5 |
注册路由:/login、/logout;/console、/api/*、/admin/* 挂 AuthMiddleware |
config/route.php |
| 6 |
权限:按 users.role 控制(客服=建单/分派/关闭;客服主管=+取消审批/报表;运维=+接单/处理/回填;管理员=+系统配置/报表) |
app/controller/* |
| 7 |
配置:config/app.php 或 .env 增加 OAUTH_BASE_URL / OAUTH_CLIENT_ID / OAUTH_CLIENT_SECRET |
config/app.php |
| 8 |
前端适配:console.html 顶部显示登录用户名+角色;侧栏按角色显隐菜单 |
public/console.html、assets/console.js |
M2:令牌生命周期
- Session 存
oauth_access_token/oauth_refresh_token/oauth_expires_at
AuthMiddleware 检测临近过期(剩 10 分钟内)→ refreshToken() 换新 → 更新 Session
- 登出:清 Session(可选调 oauth 撤销令牌接口——oauth 无独立 revoke HTTP 端点,撤销走后台管理,一期仅本地清 Session)
M3:二期能力(RBAC 权限码已完成 ✅)
- oauth 后台为
woc client 配置权限节点树(dashboard / workorder:list/create/assign/process/close/cancel / dispatch:view / admin:sla/users/stations/events/stats)+ 角色(admin/cs_leader/cs/ops)+ 用户分配 ✅
- 工单中心
AuthMiddleware 改为按 Session 缓存的权限码逐接口校验 ✅
- 令牌刷新时同步重拉权限码(refresh_permissions 语义)✅
- 剩余:外部系统开放 API(
/open/v1)的 Bearer 校验仍为演示映射(factory_ 前缀),需改为调 oauth userinfo 校验 + access_permissions 鉴权,并将 woc 的 client_credentials 令牌发放给车厂等外部系统
六、验证
| # |
场景 |
预期 |
| 1 |
未登录访问 /console、/api/work-orders |
302 → /login |
| 2 |
登录页提交 demo/123456(oauth 用户) |
登录成功 → /console,顶部显示用户名+角色 |
| 3 |
登录页提交错误密码 |
明确错误提示;连续 5 次失败 oauth 侧锁定 |
| 4 |
各角色访问权限外接口 |
403(如 ops 访问 /admin/sla-config) |
| 5 |
令牌过期 |
自动刷新,用户无感 |
| 6 |
登出后再次访问受保护页 |
需重新登录 |
| 7 |
oauth 服务不可达 |
登录页提示"认证服务不可用" |
七、前置条件与风险
| 项 |
状态 |
oauth 服务运行中(8788)且 woc client 已注册(grant_types 含 password) |
需运维/管理员操作 |
users 表需补 oauth_user_id/username/password 列 |
需执行迁移 |
| 工单中心与 oauth 的账号映射(现有 users 记录如何对到 oauth 账号) |
需确认:name 与 oauth username 是否一致,或手动建档 |
| 本地开发环境无独立 oauth 服务时 |
可用 oauth 项目的 SQLite 库 demo/123456 直连校验(password 存本地 users 表,同 bcrypt 算法) |
附录:关键接口速查
| 方法 |
路径 |
参数 |
返回 |
| POST |
/token |
grant_type=password, client_id, client_secret, username, password |
access_token, refresh_token, expires_in |
| POST |
/token |
grant_type=refresh_token, client_id, client_secret, refresh_token |
新 access/refresh token |
| GET |
/userinfo |
Authorization: Bearer <token> |
user(id/username/nickname), scope |
| GET |
/user-permissions |
Bearer |
user, roles, permissions, nodes |
| POST |
/token |
grant_type=access_permissions, client_id, client_secret, access_token, client_permission |
allowed: true/false |