2026-07-16代码优化.md
7.28 KB
2026-07-16 泰额版代码优化
本文档说明 泰额版后端 当日对 tenant-select 出参、默认管理员凭据加密及 Web 登录入参的改造。仅涉及泰额版 Yi.Abp.Net8,前端需同步对接。
一、变更概述
| 项 | 说明 |
|---|---|
| 接口 | GET /api/app/th-multi-tenancy/tenant-select |
| 目的 | 登录页展示租户列表时不再暴露租户 Id、数据库连接串;改为返回创建时间与可解密的默认管理员凭据 |
| 登录 | POST /api/app/th-web-auth/login 支持 tenantName 替代 tenantId |
| 配置 | 新增 FoodLabeling:TenantSelectCrypto:SecretKey |
二、tenant-select 出参变更
2.1 新旧字段对比
| 旧字段 | 新字段 | 说明 |
|---|---|---|
id |
— | 已移除,不再返回租户 Guid |
name |
name |
保留,租户名称 |
| — | creationTime |
新增,租户创建时间(主库 yitenant) |
| — | loginAccount |
新增,默认管理员账号,固定为 admin(与 UserDataSeed 一致) |
| — | password |
新增,AES-CBC 加密后的 Base64 密文(非库内不可逆哈希) |
| — | passwordSalt |
新增,AES IV 的 Base64,前端解密时使用 |
tenantConnectionString / 连接串相关 |
— | 从未在此接口返回;仍不暴露 |
2.2 出参示例
[
{
"name": "Default",
"creationTime": "2026-01-15T10:30:00",
"loginAccount": "admin",
"password": "xK3mP9vL2nQ8wR1sT4uV7yZ0aB5cD6eF8gH9iJ0kL1m=",
"passwordSalt": "AbCdEfGhIjKlMnOpQrStUvWxYz0123=="
}
]
每条记录的
password/passwordSalt因随机 IV 而不同;明文来源均为RbacOptions.AdminPassword(当前默认123456)。
三、密码加盐加密与前端解密
3.1 算法约定
| 项 | 值 |
|---|---|
| 算法 | AES-256-CBC |
| 填充 | PKCS7 |
| Key | SHA256(UTF8(SecretKey)) → 32 字节 |
| IV | 每次加密随机 16 字节,Base64 写入 passwordSalt |
| 密文 | Base64 写入 password |
| 明文 | RbacOptions.AdminPassword(appsettings,与种子管理员一致) |
3.2 后端配置
appsettings.json:
"FoodLabeling": {
"TenantSelectCrypto": {
"SecretKey": "FoodLabelingThTenantSelect2026Key!"
}
}
SecretKey为前后端约定的字符串,不要求本身为 32 字节;后端用 SHA256 派生 AES-256 密钥。- 生产环境请更换为足够长度的随机字符串,并与前端构建配置保持一致。
3.3 前端解密(CryptoJS 示例)
import CryptoJS from 'crypto-js';
const SECRET_KEY = 'FoodLabelingThTenantSelect2026Key!'; // 与后端 TenantSelectCrypto:SecretKey 一致
function decryptTenantPassword(passwordBase64, passwordSaltBase64) {
const key = CryptoJS.SHA256(CryptoJS.enc.Utf8.parse(SECRET_KEY));
const iv = CryptoJS.enc.Base64.parse(passwordSaltBase64);
const cipher = CryptoJS.enc.Base64.parse(passwordBase64);
const decrypted = CryptoJS.AES.decrypt(
{ ciphertext: cipher },
key,
{ iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 },
);
return decrypted.toString(CryptoJS.enc.Utf8);
}
3.4 前端解密(Web Crypto 伪代码)
async function decryptTenantPassword(passwordBase64, passwordSaltBase64) {
const secretKey = 'FoodLabelingThTenantSelect2026Key!';
const keyMaterial = await crypto.subtle.importKey(
'raw',
await crypto.subtle.digest('SHA-256', new TextEncoder().encode(secretKey)),
{ name: 'AES-CBC' },
false,
['decrypt'],
);
const iv = Uint8Array.from(atob(passwordSaltBase64), (c) => c.charCodeAt(0));
const cipher = Uint8Array.from(atob(passwordBase64), (c) => c.charCodeAt(0));
const plain = await crypto.subtle.decrypt({ name: 'AES-CBC', iv }, keyMaterial, cipher);
return new TextDecoder().decode(plain);
}
3.5 安全说明
- 接口为 匿名可访问,密文仅降低明文密码在传输/展示中的直接暴露风险,不能替代 HTTPS 与生产环境改密策略。
- 解密后的密码仅用于登录表单预填或一键登录;请勿持久化明文密码。
四、Web 登录:tenantName 替代 tenantId
tenant-select 不再返回 id 后,Web 登录须使用 租户名称。
4.1 入参变更
| 字段 | 必填 | 说明 |
|---|---|---|
tenantId |
否 | 与 tenantName 二选一 |
tenantName |
否 | 与 tenantId 二选一,推荐 |
userName |
是 | 账号或邮箱;admin 或 admin@example.com 均可 |
password |
是 | 明文密码 |
二者均为空时返回:请指定租户 Id 或租户名称。
4.2 登录示例(curl)
curl -X POST "http://127.0.0.1:19002/api/app/th-web-auth/login" \
-H "Content-Type: application/json" \
-d "{\"tenantName\":\"Default\",\"userName\":\"admin\",\"password\":\"123456\"}"
4.3 出参(不变)
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "...",
"tenantId": "11111111-1111-1111-1111-111111111111",
"tenantName": "Default"
}
Token 内仍写入真实 TenantId Guid;后续业务 API 行为不变。
五、泰额 App 登录说明
POST /api/app/th-app-auth/login 本次未改,仍要求入参 tenantId(Guid)。
若 App 端也需要去掉 tenantId,需单独改造 ThAppLoginInputVo 与 ThAppAuthAppService,并与移动端联调。
六、相关接口(当日其它优化,可选)
- 开通租户
POST /api/app/th-tenant-provisioning/provision:创建租户后可在后台异步初始化业务库(DatabaseInitializing/InitializeTenantDatabase)。 - 同步初始化
POST /api/app/th-tenant-provisioning/initialize-tenant-database/{tenantId}:补跑租户库 CodeFirst + Seed。
与本次 tenant-select 改造无直接冲突;平台管理员仍通过 provision 创建租户,登录页通过 tenant-select + tenantName 登录。
七、改动文件列表(后端)
| 文件 | 变更 |
|---|---|
FoodLabeling.Th.Application.Contracts/Dtos/MultiTenancy/ThTenantSelectDto.cs |
出参字段调整 |
FoodLabeling.Th.Application.Contracts/Options/FoodLabelingThTenantSelectCryptoOptions.cs |
新增加密配置 |
FoodLabeling.Th.Application/MultiTenancy/TenantSelectCredentialCipher.cs |
AES-CBC 加密 |
FoodLabeling.Th.Application/MultiTenancy/ThTenantSelectConsts.cs |
默认 admin 账号常量 |
FoodLabeling.Th.Application/MultiTenancy/TenantResolveHelper.cs |
支持 tenantName 解析 |
FoodLabeling.Th.Application/Services/ThMultiTenancyAppService.cs |
tenant-select 映射与加密 |
FoodLabeling.Th.Application.Contracts/Dtos/Auth/ThWebLoginInputVo.cs |
TenantId 可选 + TenantName |
FoodLabeling.Th.Application/Services/ThWebAuthAppService.cs |
按名称登录 |
FoodLabeling.Th.Application/FoodLabelingThApplicationModule.cs |
注册加密配置 |
src/Yi.Abp.Web/appsettings.json |
TenantSelectCrypto 节点 |
八、前端对接 checklist
- [ ] 更新
ThTenantSelectDto类型,移除id,增加creationTime/loginAccount/password/passwordSalt - [ ] 配置与后端一致的
SecretKey,解密password后填入登录表单(若需) - [ ] 登录请求改传
tenantName: selected.name,不再依赖tenantId - [ ] 登录成功后仍保存响应中的
tenantId供业务 API /__tenant头使用