diff --git a/README.md b/README.md index 3b816d10..c152ffbf 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ API_PROXY_TARGET=http://8.163.30.128:9000 客户代登录打开的工作台地址是运行时配置,不需要为每个环境重新构建镜像。Docker 部署时设置容器环境变量 `CLIENT_WEB_URL`,例如 `http://8.163.30.128:9002`;容器启动时会生成 `/runtime-config.js`。本地 Vite 开发才使用 `VITE_CLIENT_WEB_URL` 作为兜底,当前 Client 默认端口为 `5174`。 -认证请求使用 `/api/auth/**`,平台 IAM 请求使用 `/api/platform/v1/iam/**`。登录后从 JWT 读取平台身份和授权快照,再与 IAM 返回的启用权限资源取交集。企业审核调用 `/reviews`,打开审核详情后按租户调用 `/tenants/{tenantId}/qualifications`;营业执照通过 `/api/platform/v1/files/qualifications/{tenantId}/{fileName}` 携带平台 Token 临时读取,关闭预览后释放本地对象地址。客户端自主注册企业审核通过后继续使用注册账号作为管理员,不展示新凭据;Admin 代建企业并由后端创建管理员时,页面必须展示不可通过遮罩、Esc 或关闭按钮跳过的一次性凭据弹窗,运营人员确认保存后才清除账号密码。平台角色查询、新增、编辑、启停、删除和权限全量保存均已按 `RbacController` 接入真实 `/roles` 契约,角色返回的 `permissionIds` 用于授权回显;系统内置角色及仍被成员使用的角色继续由服务端保护。权限资源查询和新增调用 `/permissions`,尚未提供的权限编辑、删除和启停不会模拟成功。一期真实权限资源只创建 `MENU` 与 `BUTTON`,不创建 `PAGE` 或 `API` 类型。其余业务页面需在对应服务契约确认后逐项替换,生产逻辑不得静默回退到模拟数据。 +认证请求使用 `/api/auth/**`,平台 IAM 请求使用 `/api/platform/v1/iam/**`。登录后只从 JWT 读取平台身份,菜单和操作权限通过当前用户授权接口实时加载,不从 JWT 携带或解析角色权限。企业审核调用 `/reviews`,打开审核详情后按租户调用 `/tenants/{tenantId}/qualifications`;营业执照通过 `/api/platform/v1/files/qualifications/{tenantId}/{fileName}` 携带平台 Token 临时读取,关闭预览后释放本地对象地址。客户端自主注册企业审核通过后继续使用注册账号作为管理员,不展示新凭据;Admin 代建企业并由后端创建管理员时,页面必须展示不可通过遮罩、Esc 或关闭按钮跳过的一次性凭据弹窗,运营人员确认保存后才清除账号密码。平台角色查询、新增、编辑、启停、删除和权限全量保存均已按 `RbacController` 接入真实 `/roles` 契约,角色返回的 `permissionIds` 用于授权回显;系统内置角色及仍被成员使用的角色继续由服务端保护。权限资源查询和新增调用 `/permissions`,尚未提供的权限编辑、删除和启停不会模拟成功。一期真实权限资源只创建 `MENU` 与 `BUTTON`,不创建 `PAGE` 或 `API` 类型。其余业务页面需在对应服务契约确认后逐项替换,生产逻辑不得静默回退到模拟数据。 仓库管理已接入真实平台 Warehouse API,统一维护国内收货仓、FBA 仓和海外仓,支持按租户筛选、详情、新增、编辑和启停,并在更新时保留后端返回的地址、能力和截单时间等完整字段。停用仓库保留历史数据,但不再提供给新业务选择;订单详情使用下单时保存的仓库编码、名称和地址快照,仓库主数据后续修改不改变历史展示。库存和出入库作业由对应业务模块管理,不在仓库主数据页面中模拟。 diff --git a/src/core/auth/auth.test.ts b/src/core/auth/auth.test.ts index 4d0e2aac..2f165412 100644 --- a/src/core/auth/auth.test.ts +++ b/src/core/auth/auth.test.ts @@ -43,8 +43,6 @@ function token(overrides: Record = {}) { user_id: '1000001', username: 'platform_admin', app_code: 'PLATFORM', - roles: ['PLATFORM_ADMIN'], - permissions: ['platform:warehouse:list', 'platform:order:list'], exp: Math.floor(Date.now() / 1000) + 1800, ...overrides, } @@ -75,7 +73,7 @@ describe('admin auth session', () => { mocks.refreshHandler = null }) - it('intersects JWT permissions with the current authenticated permission snapshot', async () => { + it('loads permissions from the current authorization endpoint when the JWT only contains identity', async () => { mocks.post.mockResolvedValue(loginResponse()) mocks.listCurrentPlatformPermissionCodes.mockResolvedValue(['platform:warehouse:list']) const auth = useAuthStore() @@ -134,7 +132,7 @@ describe('admin auth session', () => { localStorage.setItem('cc-admin-refresh-token', 'refresh-old') localStorage.setItem('cc-admin-permission-codes', '["platform:warehouse:list"]') const refreshedToken = token({ - permissions: ['platform:warehouse:list', 'platform:firstleg:inbound:list'], + token_version: 2, }) mocks.post.mockResolvedValue(loginResponse(refreshedToken, 'refresh-rotated')) mocks.listCurrentPlatformPermissionCodes.mockResolvedValue([ diff --git a/src/core/auth/auth.ts b/src/core/auth/auth.ts index 137c813b..34c60156 100644 --- a/src/core/auth/auth.ts +++ b/src/core/auth/auth.ts @@ -39,7 +39,6 @@ function sessionFromToken(token: string) { account: claims.username || '', roles: claims.roles ?? [], } satisfies CurrentUser, - permissions: claims.permissions ?? [], } } @@ -58,7 +57,7 @@ export const useAuthStore = defineStore('admin-auth', () => { const cachedPermissions = JSON.parse(localStorage.getItem(PERMISSION_CODES_KEY) || '[]') as unknown if (!Array.isArray(cachedPermissions)) throw new Error('权限缓存格式无效') user.value = session.user - permissions.value = session.permissions.filter((code) => cachedPermissions.includes(code)) + permissions.value = cachedPermissions.filter((code): code is string => typeof code === 'string') isServerSession.value = true } catch (error) { token.value = '' @@ -98,8 +97,7 @@ export const useAuthStore = defineStore('admin-auth', () => { const activePermissionCodes = await listCurrentPlatformPermissionCodes() ensureCurrentGeneration(expectedGeneration) permissionResources.value = [] - const activePermissionCodeSet = new Set(activePermissionCodes) - applyPermissionCodes(session.permissions.filter((code) => activePermissionCodeSet.has(code))) + applyPermissionCodes(activePermissionCodes) } catch (error) { ensureCurrentGeneration(expectedGeneration) throw error @@ -138,7 +136,9 @@ export const useAuthStore = defineStore('admin-auth', () => { token.value = storedToken refreshToken.value = storedRefreshToken user.value = session.user - permissions.value = session.permissions.filter((code) => Array.isArray(cachedPermissions) && cachedPermissions.includes(code)) + permissions.value = Array.isArray(cachedPermissions) + ? cachedPermissions.filter((code): code is string => typeof code === 'string') + : [] permissionResources.value = [] isServerSession.value = true } catch {