# 用户权限校验实现计划 ## Context 当前系统零认证,所有 API 完全开放。需要接入公司现有的 jumpToken 认证体系,并根据用户角色实现数据权限过滤。用户从资产管理平台跳转进入本系统,携带 jumpToken。 ## 认证流程 ``` 用户带 jumpToken 访问 → 前端提取 jumpToken → 后端代理调用外部API换取 sessionToken → 后端调用外部API获取用户信息(roles, depCode) → 后端签发 JWT 返回前端 → 前端所有请求带 JWT → 后端中间件验证 ``` ## 三级权限模型 | 级别 | 角色条件 | 数据范围 | |------|---------|---------| | **full** | roleName 含 "所有权限" / "数智中心" / "BI-Leader" | 全部数据 | | **department** | roleName 含 "BI-Leader-Dep" | 自己部门的全部数据 | | **personal** | 无以上角色 | 仅自己负责的车辆(bd=userId) | 部门匹配:用户 depCode → 查 tab_department 得 dep_name → 过滤车辆数据中的 department 字段 个人匹配:添加 managerId 字段(c.bd),按 userId 精确匹配(不用 userName 避免重名) ## 新增/修改文件 ### 后端新增 - `src/server/auth/types.ts` — AuthUser、PermissionLevel、JwtPayload 类型 - `src/server/auth/login.ts` — `POST /api/auth/login`(接收外部token,调外部API获取用户信息,签发JWT)+ `GET /api/auth/exchange`(代理 jumpToken 换取 sessionToken,避免前端 CORS 问题) - `src/server/auth/middleware.ts` — Hono 中间件,验证 JWT,跳过 /api/health 和 /api/auth/* - `src/server/auth/permissions.ts` — `filterByPermission(items, user)` 通用过滤函数 ### 后端修改 - `src/server/index.ts` — 挂载 auth 路由和中间件 - `src/server/routes/mileage/vehicle-info.ts` — SQL 添加 `c.bd AS manager_id` - `src/server/routes/mileage/types.ts` — CachedVehicle 添加 `managerId: number | null` - `src/server/routes/mileage/cache.ts` — 传递 managerId - `src/server/routes/mileage/monitoring.ts` — 请求时 filterByPermission + 重算筛选选项 - `src/server/routes/mileage/targets.ts` — 按权限过滤 - `src/server/routes/mileage/trend.ts` — 按权限限定车牌范围 - `src/server/routes/vehicles.ts` — 所有端点用 `getVehiclesForUser(c)` 替代 `getVehicles()` - `src/server/types.ts` — Vehicle 类型添加 managerId ### 前端新增 - `src/auth/AuthProvider.tsx` — 认证上下文,管理 jumpToken 交换和 JWT 存储 - `src/auth/useAuth.ts` — 认证状态 hook - `src/auth/api-client.ts` — 全局 fetchJson,自动附加 Authorization header - `src/auth/UnauthorizedPage.tsx` — 未授权页面(图标 + 提示文字) ### 前端修改 - `src/App.tsx` — 包裹 AuthProvider,条件渲染 Shell / UnauthorizedPage - `src/modules/mileage/api.ts` — fetchJson 改用 auth/api-client - `src/modules/assets/api.ts` — fetchJson 改用 auth/api-client ### 环境配置 - `.env` 添加 `JWT_SECRET`、`EXTERNAL_API_BASE` - `package.json` 添加 `jsonwebtoken`、`@types/jsonwebtoken` ## 关键设计决策 1. **缓存全局,请求时过滤** — 监控缓存保持全量数据,每次请求根据用户权限过滤,筛选选项也从过滤后数据重算 2. **jumpToken 交换走后端代理** — 避免前端 CORS 问题,外部 API 调用全部在服务端 3. **用 managerId(数字ID)匹配而非 userName** — 避免重名问题 4. **JWT 存 sessionStorage** — 刷新页面不丢失,关闭标签页自动清除 5. **filterByPermission 泛型函数** — 同时适配 Vehicle 和 CachedVehicle 类型 ## 实施顺序 1. 安装依赖 (`jsonwebtoken`) 2. 后端 auth 模块(types → login → middleware → permissions) 3. 数据模型添加 managerId(vehicle-info → types → cache) 4. 挂载中间件到 server/index.ts 5. 集成权限过滤到各路由(vehicles.ts, monitoring, targets, trend) 6. 前端 auth 模块(AuthProvider, useAuth, api-client, UnauthorizedPage) 7. 前端 API 模块切换到 auth fetch 8. App.tsx 添加认证网关 9. 端到端测试三个权限级别 ## 验证方式 1. 无 jumpToken 访问 → 显示"未授权访问"页面 2. 带有效 jumpToken 访问 → 正常进入,检查 JWT 签发 3. full 角色用户 → 看到全部 1004 辆车数据 4. department 角色用户 → 仅看到自己部门的车辆 5. personal 用户 → 仅看到自己负责的车辆 6. JWT 过期后请求 → 返回 401,前端显示未授权 7. 全屏监控筛选选项 → 仅显示用户权限范围内的部门/客户