feat: 实现历史API Key自动关联功能

核心功能:
- AD用户登录时自动关联已存在的历史API Key
- 关联规则: API Key name字段与用户displayName完全匹配
- 自动设置owner字段完成关联,避免用户重新创建Key

实现逻辑:
1. 优先匹配owner字段(已关联的Key)
2. 如无owner匹配,尝试匹配name与displayName
3. 找到匹配历史Key后,自动设置owner完成关联

技术特性:
- 详细日志记录关联过程
- 支持JWT token中完整用户信息传递
- Redis数据自动更新owner字段
- 系统迁移兼容性处理

测试验证:
- 创建测试历史Key验证自动关联
- JWT token正确解析displayName字段
- Redis数据正确更新owner关联关系

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
iRubbish
2025-08-25 18:19:33 +08:00
parent 7624c383e8
commit f31f7c9385
3 changed files with 103 additions and 18 deletions

View File

@@ -384,24 +384,41 @@ const authenticateUser = (req, res, next) => {
/**
* 获取用户的API Keys
*
* 自动关联逻辑说明:
* 系统迁移过程中存在历史API Key这些Key是在AD集成前手动创建的
* 创建时使用的name字段恰好与AD用户的displayName一致
* 例如: AD用户displayName为"测试用户"对应的API Key name也是"测试用户"
* 为了避免用户重复创建Key系统会自动关联这些历史Key
* 关联规则:
* 1. 优先匹配owner字段(新建的Key)
* 2. 如果没有owner匹配则尝试匹配name字段与displayName
* 3. 找到匹配的历史Key后自动将owner设置为当前用户完成关联
*/
router.get('/user/api-keys', authenticateUser, async (req, res) => {
try {
const redis = require('../models/redis')
const { username } = req.user
const { username, displayName } = req.user
logger.info(`获取用户API Keys: ${username}`)
logger.info(`获取用户API Keys: ${username}, displayName: ${displayName}`)
logger.info(`用户完整信息: ${JSON.stringify(req.user)}`)
// 获取所有API Keys
const allKeysPattern = 'api_key:*'
const keys = await redis.getClient().keys(allKeysPattern)
const userKeys = []
let foundHistoricalKey = false
// 筛选属于该用户的API Keys
for (const key of keys) {
const apiKeyData = await redis.getClient().hgetall(key)
if (apiKeyData && apiKeyData.owner === username) {
if (!apiKeyData) {
continue
}
// 规则1: 直接owner匹配(已关联的Key)
if (apiKeyData.owner === username) {
userKeys.push({
id: apiKeyData.id,
name: apiKeyData.name || '未命名',
@@ -412,6 +429,30 @@ router.get('/user/api-keys', authenticateUser, async (req, res) => {
status: apiKeyData.status || 'active'
})
}
// 规则2: 历史Key自动关联(name字段匹配displayName且无owner)
else if (displayName && apiKeyData.name === displayName && !apiKeyData.owner) {
logger.info(`发现历史API Key需要关联: name=${apiKeyData.name}, displayName=${displayName}`)
// 自动关联: 设置owner为当前用户
await redis.getClient().hset(key, 'owner', username)
foundHistoricalKey = true
userKeys.push({
id: apiKeyData.id,
name: apiKeyData.name || '未命名',
key: apiKeyData.key,
limit: parseInt(apiKeyData.limit) || 1000000,
used: parseInt(apiKeyData.used) || 0,
createdAt: apiKeyData.createdAt,
status: apiKeyData.status || 'active'
})
logger.info(`历史API Key关联成功: ${apiKeyData.id} -> ${username}`)
}
}
if (foundHistoricalKey) {
logger.info(`用户 ${username} 自动关联了历史API Key`)
}
res.json({