import fs from 'node:fs' import path from 'node:path' import assert from 'node:assert/strict' import { fileURLToPath } from 'node:url' import { createRequire } from 'node:module' import { login, call } from './role-permissions-live.mjs' const report = fileURLToPath(new URL('../reports/digital-human-simple-auth-20260909/', import.meta.url)) fs.mkdirSync(report, { recursive:true }) const checks = [] function check(name, condition) { assert.ok(condition, name); checks.push({ name, result:'PASS' }); console.log(`PASS: ${name}`) } const clean = value => !/"(?:appSecret|secretCipher|app_secret|secret_cipher)"\s*:/.test(JSON.stringify(value)) let browser try { for (const name of ['integration-secret.key', 'integration-internal.key']) { check(`${name} 已移除`, !fs.existsSync(fileURLToPath(new URL(`../../unreal_tran_api/config/${name}`, import.meta.url)))) } const session = await login('root') const token = session.accessToken const configs = await call('auth/v1/interfaces?size=100', token) check('接口配置列表不返回 Secret 字段', configs.status === 200 && clean(configs.data)) const row = configs.data.records.find(item => item.code === 'digital-human' && item.environmentCode === 'default') assert.ok(row, 'Digital-human configuration exists') const settings = JSON.parse(row.extraConfig) check('原有 APPID 和密钥配置保留', settings.appId === 'AP_238179E0A10F97927EA49FA3' && settings.secretConfigured) const configuration = { ...row, environment:row.environmentCode, version:row.dataVersion } const saved = await call(`auth/v1/interfaces/${row.id}/digital-human`, token, { configuration, appId:settings.appId, appSecret:'' }, 'PUT') check('留空 Secret 保存配置成功且不回显密钥', saved.status === 200 && saved.code === 0 && clean(saved.data)) const tested = await call(`auth/v1/interfaces/${row.id}/test`, token, {}) check('移除密钥文件后 APPID + Secret 连接测试成功', tested.status === 200 && tested.data?.success) const runtimeUrl = 'http://127.0.0.1:6101/internal/v1/interfaces/digital-human/runtime' const anonymous = await fetch(runtimeUrl, { method:'POST' }) check('内部调用继续要求现有登录身份', anonymous.status === 401) const runtime = await fetch(runtimeUrl, { method:'POST', headers:{ Authorization:`Bearer ${token}` } }) const runtimeData = await runtime.json() check('复用登录身份获取运行令牌,无额外服务密钥', runtime.status === 200 && runtimeData.data?.accessToken && clean(runtimeData) && runtime.headers.get('cache-control') === 'no-store') const capabilities = await call('tran/v1/content/digital-human/capabilities', token) check('Tran → Auth → 数字人系统能力调用成功', capabilities.status === 200 && capabilities.data?.appId === settings.appId) for (const kind of ['videos', 'video-folders', 'avatars', 'voices', 'backgrounds']) { const response = await call(`tran/v1/content/digital-human/resources/${kind}`, token) check(`授权资源 ${kind} 查询成功`, response.status === 200 && response.code === 0) } const { chromium, expect } = createRequire(new URL('../package.json', import.meta.url))('@playwright/test') browser = await chromium.launch({ channel:'msedge', headless:true }) const context = await browser.newContext({ viewport:{ width:1600, height:1000 } }) await context.addInitScript(({ accessToken, refreshToken }) => { sessionStorage.setItem('unreal-tran:web:access-token:v1', accessToken) sessionStorage.setItem('unreal-tran:web:refresh-token:v1', refreshToken) }, session) const page = await context.newPage() await page.goto('http://127.0.0.1:6180/system/interfaces') const configRow = page.locator('.el-table__body tr').filter({ hasText:'digital-human' }).first() await expect(configRow).toBeVisible({ timeout:60000 }) await configRow.getByRole('button', { name:'编辑配置' }).click() const dialog = page.getByRole('dialog', { name:'编辑服务配置' }) const secret = dialog.locator('input[type="password"]') await expect(secret).toHaveValue('') await expect(secret).toHaveAttribute('placeholder', '已配置,留空保留现有密钥') check('配置弹窗密钥留空,仅显示已配置状态', true) await page.screenshot({ path:path.join(report, '01-configuration.png'), animations:'disabled' }) const saveResponse = page.waitForResponse(response => response.request().method() === 'PUT' && response.url().endsWith(`/interfaces/${row.id}/digital-human`)) await dialog.getByRole('button', { name:'保存配置' }).click() check('页面留空密钥保存成功', (await saveResponse).status() === 200) await expect(dialog).toBeHidden() const testResponse = page.waitForResponse(response => response.request().method() === 'POST' && response.url().endsWith(`/interfaces/${row.id}/test`)) await configRow.getByRole('button', { name:'连接检查' }).click() const testedFromPage = await (await testResponse).json() check('页面连接检查实际响应成功', testedFromPage.data?.success) await expect(configRow.getByText('连接正常', { exact:true })).toBeVisible({ timeout:30000 }) await expect(page.locator('.el-loading-mask:visible')).toHaveCount(0) await page.screenshot({ path:path.join(report, '02-connection.png'), animations:'disabled' }) check('页面连接测试成功', true) } finally { await browser?.close() fs.writeFileSync(path.join(report, 'results.json'), JSON.stringify({ time:new Date().toISOString(), checks }, null, 2)) } fs.writeFileSync(path.join(report, 'index.html'), `数字人 APPID + Secret 简化验证

数字人 APPID + Secret 简化验证

2026-09-09 · 开发环境真实联调 · ${checks.length} 项通过

移除两个额外密钥文件及运行依赖;现有 Secret 迁移至接口配置表 app_secret。继续使用现有登录身份调用 Auth,数字人开放服务通过 APPID + Secret 换取短期令牌。

后端 mvn clean package 通过;新增认证、密钥保存和转发测试。039 迁移的密文保护、正常迁移、重复执行已验证。原有应用保持不变;本轮未重新创建视频生成任务。

配置弹窗

真实连接测试

`)