feat: 改进 Gemini token 刷新测试脚本和添加解密测试脚本

- 修正加密盐值为 'gemini-account-salt'(之前错误使用了其他值)
- 在 test-gemini-refresh.js 中添加详细的调试信息
  - 显示配置信息和加密参数
  - 显示原始加密数据
  - 尝试手动解密并显示结果
  - 更详细的错误信息
- 添加 test-gemini-decrypt.js 用于单独测试解密功能

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
shaw
2025-07-26 13:56:52 +08:00
parent 24858f7e51
commit 25ab1575ed
2 changed files with 160 additions and 1 deletions

View File

@@ -0,0 +1,102 @@
#!/usr/bin/env node
/**
* 测试 Gemini 账户解密
*/
const path = require('path');
const dotenv = require('dotenv');
const crypto = require('crypto');
// 加载环境变量
dotenv.config({ path: path.join(__dirname, '..', '.env') });
const redis = require('../src/models/redis');
const config = require('../config/config');
const ALGORITHM = 'aes-256-cbc';
const ENCRYPTION_SALT = 'gemini-account-salt'; // 正确的盐值!
const GEMINI_ACCOUNT_KEY_PREFIX = 'gemini_account:';
// 生成加密密钥(与 geminiAccountService 完全相同)
function generateEncryptionKey() {
return crypto.scryptSync(config.security.encryptionKey, ENCRYPTION_SALT, 32);
}
// 解密函数(与 geminiAccountService 相同)
function decrypt(text) {
if (!text) return '';
try {
const key = generateEncryptionKey();
// IV 是固定长度的 32 个十六进制字符16 字节)
const ivHex = text.substring(0, 32);
const encryptedHex = text.substring(33); // 跳过冒号
const iv = Buffer.from(ivHex, 'hex');
const encryptedText = Buffer.from(encryptedHex, 'hex');
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
let decrypted = decipher.update(encryptedText);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString();
} catch (error) {
console.error('解密错误:', error.message);
return null;
}
}
async function testDecrypt() {
try {
console.log('🚀 测试 Gemini 账户解密...\n');
console.log('📋 加密配置:');
console.log(` config.security.encryptionKey: ${config.security.encryptionKey}`);
console.log(` ENCRYPTION_SALT: ${ENCRYPTION_SALT}`);
console.log();
// 连接 Redis
console.log('📡 连接 Redis...');
await redis.connect();
console.log('✅ Redis 连接成功\n');
const client = redis.getClient();
const keys = await client.keys(`${GEMINI_ACCOUNT_KEY_PREFIX}*`);
if (keys.length === 0) {
console.log('❌ 没有找到 Gemini 账户');
process.exit(1);
}
console.log(`🔍 找到 ${keys.length} 个 Gemini 账户\n`);
for (const key of keys) {
const accountData = await client.hgetall(key);
const accountId = key.replace(GEMINI_ACCOUNT_KEY_PREFIX, '');
console.log(`📋 账户: ${accountData.name} (${accountId})`);
if (accountData.refreshToken) {
console.log('🔐 尝试解密 refreshToken...');
const decrypted = decrypt(accountData.refreshToken);
if (decrypted) {
console.log('✅ 解密成功!');
console.log(` Token 前缀: ${decrypted.substring(0, 20)}...`);
} else {
console.log('❌ 解密失败');
}
} else {
console.log('⚠️ 无 refreshToken');
}
console.log();
}
} catch (error) {
console.error('❌ 测试失败:', error);
} finally {
await redis.disconnect();
process.exit(0);
}
}
testDecrypt();

View File

@@ -13,11 +13,47 @@ dotenv.config({ path: path.join(__dirname, '..', '.env') });
const redis = require('../src/models/redis'); const redis = require('../src/models/redis');
const geminiAccountService = require('../src/services/geminiAccountService'); const geminiAccountService = require('../src/services/geminiAccountService');
const logger = require('../src/utils/logger'); const logger = require('../src/utils/logger');
const crypto = require('crypto');
const config = require('../config/config');
// 加密相关常量(与 geminiAccountService 保持一致)
const ALGORITHM = 'aes-256-cbc';
const ENCRYPTION_SALT = 'gemini-account-salt'; // 注意:是 'gemini-account-salt' 不是其他值!
// 生成加密密钥
function generateEncryptionKey() {
return crypto.scryptSync(config.security.encryptionKey, ENCRYPTION_SALT, 32);
}
// 解密函数(用于调试)
function debugDecrypt(text) {
if (!text) return { success: false, error: 'Empty text' };
try {
const key = generateEncryptionKey();
const ivHex = text.substring(0, 32);
const encryptedHex = text.substring(33);
const iv = Buffer.from(ivHex, 'hex');
const encryptedText = Buffer.from(encryptedHex, 'hex');
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
let decrypted = decipher.update(encryptedText);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return { success: true, value: decrypted.toString() };
} catch (error) {
return { success: false, error: error.message };
}
}
async function testGeminiTokenRefresh() { async function testGeminiTokenRefresh() {
try { try {
console.log('🚀 开始测试 Gemini token 刷新功能...\n'); console.log('🚀 开始测试 Gemini token 刷新功能...\n');
// 显示配置信息
console.log('📋 配置信息:');
console.log(` 加密密钥: ${config.security.encryptionKey}`);
console.log(` 加密盐值: ${ENCRYPTION_SALT}`);
console.log();
// 1. 连接 Redis // 1. 连接 Redis
console.log('📡 连接 Redis...'); console.log('📡 连接 Redis...');
await redis.connect(); await redis.connect();
@@ -41,6 +77,26 @@ async function testGeminiTokenRefresh() {
console.log(` 状态: ${account.status}`); console.log(` 状态: ${account.status}`);
try { try {
// 获取原始账户数据(用于调试)
const client = redis.getClient();
const rawData = await client.hgetall(`gemini_account:${account.id}`);
console.log(' 📊 原始数据检查:');
console.log(` refreshToken 存在: ${rawData.refreshToken ? '是' : '否'}`);
if (rawData.refreshToken) {
console.log(` refreshToken 长度: ${rawData.refreshToken.length}`);
console.log(` refreshToken 前50字符: ${rawData.refreshToken.substring(0, 50)}...`);
// 尝试手动解密
const decryptResult = debugDecrypt(rawData.refreshToken);
if (decryptResult.success) {
console.log(` ✅ 手动解密成功`);
console.log(` 解密后前20字符: ${decryptResult.value.substring(0, 20)}...`);
} else {
console.log(` ❌ 手动解密失败: ${decryptResult.error}`);
}
}
// 获取完整账户信息(包括解密的 token // 获取完整账户信息(包括解密的 token
const fullAccount = await geminiAccountService.getAccount(account.id); const fullAccount = await geminiAccountService.getAccount(account.id);
@@ -49,7 +105,8 @@ async function testGeminiTokenRefresh() {
continue; continue;
} }
console.log(` ✅ 找到 refresh token`) console.log(` ✅ 找到 refresh token`);
console.log(` 📝 解密后的 refresh token 前20字符: ${fullAccount.refreshToken.substring(0, 20)}...`);
console.log(' 🔄 开始刷新 token...'); console.log(' 🔄 开始刷新 token...');
const startTime = Date.now(); const startTime = Date.now();