From 8a5d4b5d8f76c65753d9b2137668637785cd2685 Mon Sep 17 00:00:00 2001 From: iRubbish Date: Tue, 26 Aug 2025 15:55:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84AD=E5=9F=9F=E6=8E=A7?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=B3=BB=E7=BB=9F=EF=BC=8C=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 完善用户API Key创建流程,移除名称编辑权限 - 清理硬编码敏感信息,改用环境变量配置 - 在README.md和.env.example中添加AD域控配置说明 - 修复ESLint no-shadow错误 - 删除测试文件test-fixed-auto-link.js 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .env.example | 31 +++++++---- README.md | 9 ++++ src/routes/ldapRoutes.js | 23 +++++--- src/services/ldapService.js | 19 +++++-- test-fixed-auto-link.js | 54 ------------------- .../src/components/user/UserApiKeysView.vue | 46 +++++----------- 6 files changed, 73 insertions(+), 109 deletions(-) delete mode 100644 test-fixed-auto-link.js diff --git a/.env.example b/.env.example index 817731a8..85ce6d2f 100644 --- a/.env.example +++ b/.env.example @@ -15,6 +15,23 @@ ENCRYPTION_KEY=your-encryption-key-here # ADMIN_USERNAME=cr_admin_custom # ADMIN_PASSWORD=your-secure-password + +# 🏢 LDAP/Windows AD 域控认证配置(可选,用于企业内部用户登录) +# 启用LDAP认证功能 +# LDAP_ENABLED=true +# AD域控服务器地址 +# LDAP_URL=ldap://your-domain-controller-ip:389 +# 绑定用户 +# LDAP_BIND_DN=your-bind-user +# 绑定用户密码 +# LDAP_BIND_PASSWORD=your-bind-password +# 搜索基础DN +# LDAP_BASE_DN=OU=YourOU,DC=your,DC=domain,DC=com +# 用户搜索过滤器 +# LDAP_SEARCH_FILTER=(&(objectClass=user)(|(cn={username})(sAMAccountName={username}))) +# 连接超时设置 +# LDAP_TIMEOUT=10000 + # 📊 Redis 配置 REDIS_HOST=localhost REDIS_PORT=6379 @@ -45,8 +62,10 @@ LOG_MAX_FILES=5 CLEANUP_INTERVAL=3600000 TOKEN_USAGE_RETENTION=2592000000 HEALTH_CHECK_INTERVAL=60000 -TIMEZONE_OFFSET=8 # UTC偏移小时数,默认+8(中国时区) -METRICS_WINDOW=5 # 实时指标统计窗口(分钟),可选1-60,默认5分钟 +SYSTEM_TIMEZONE=Asia/Shanghai +TIMEZONE_OFFSET=8 +# 实时指标统计窗口(分钟),可选1-60,默认5分钟 +METRICS_WINDOW=5 # 🎨 Web 界面配置 WEB_TITLE=Claude Relay Service @@ -67,11 +86,3 @@ WEBHOOK_URLS=https://your-webhook-url.com/notify,https://backup-webhook.com/noti WEBHOOK_TIMEOUT=10000 WEBHOOK_RETRIES=3 -# 🏢 LDAP/AD 域控配置 -LDAP_URL=ldap://172.25.3.100:389 -LDAP_BIND_DN=LDAP-Proxy-Read -LDAP_BIND_PASSWORD=Y%77JsVK8W -LDAP_BASE_DN=OU=微店,DC=corp,DC=weidian-inc,DC=com -LDAP_SEARCH_FILTER=(&(objectClass=user)(cn={username})) -LDAP_TIMEOUT=10000 -LDAP_CONNECT_TIMEOUT=10000 \ No newline at end of file diff --git a/README.md b/README.md index 8e6cfd5a..addadc27 100644 --- a/README.md +++ b/README.md @@ -250,6 +250,15 @@ REDIS_HOST=localhost REDIS_PORT=6379 REDIS_PASSWORD= +# AD域控配置(可选,用于企业内部用户登录) +LDAP_ENABLED=true +LDAP_URL=ldap://your-domain-controller-ip:389 +LDAP_BIND_DN=your-bind-user +LDAP_BIND_PASSWORD=your-bind-password +LDAP_BASE_DN=DC=your-domain,DC=com +LDAP_SEARCH_FILTER=(&(objectClass=user)(|(cn={username})(sAMAccountName={username}))) +LDAP_TIMEOUT=10000 + # Webhook通知配置(可选) WEBHOOK_ENABLED=true WEBHOOK_URLS=https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your-key diff --git a/src/routes/ldapRoutes.js b/src/routes/ldapRoutes.js index 5b207fff..66a49cf6 100644 --- a/src/routes/ldapRoutes.js +++ b/src/routes/ldapRoutes.js @@ -196,8 +196,14 @@ router.get('/list-ous', async (req, res) => { */ router.get('/verify-ou', async (req, res) => { try { - const { ou = '微店' } = req.query - const testDN = `OU=${ou},DC=corp,DC=weidian-inc,DC=com` + const defaultOU = process.env.LDAP_DEFAULT_OU || 'YourOU' + const { ou = defaultOU } = req.query + // 使用配置的baseDN来构建测试DN,而不是硬编码域名 + const config = ldapService.getConfig() + // 从baseDN中提取域部分,替换OU部分 + const baseDNParts = config.baseDN.split(',') + const domainParts = baseDNParts.filter((part) => part.trim().startsWith('DC=')) + const testDN = `OU=${ou},${domainParts.join(',')}` logger.info(`Verifying OU exists: ${testDN}`) @@ -461,7 +467,8 @@ router.get('/user/api-keys', authenticateUser, async (req, res) => { router.post('/user/api-keys', authenticateUser, async (req, res) => { try { const { username } = req.user - const { limit } = req.body + // 用户创建的API Key不需要任何输入参数,都使用默认值 + // const { limit } = req.body // 不再从请求体获取limit // 检查用户是否已有API Key const redis = require('../models/redis') @@ -492,8 +499,8 @@ router.post('/user/api-keys', authenticateUser, async (req, res) => { const defaultName = displayName || username const keyParams = { - name: defaultName, // 忽略用户输入的name,强制使用displayName - tokenLimit: limit || 0, + name: defaultName, // 使用displayName作为API Key名称 + tokenLimit: 0, // 固定为无限制 description: `AD用户${username}创建的API Key`, // AD用户创建的Key添加owner信息以区分用户归属 owner: username, @@ -521,7 +528,7 @@ router.post('/user/api-keys', authenticateUser, async (req, res) => { id: newKey.id, key: newKey.apiKey, // 返回完整的API Key name: newKey.name, - tokenLimit: newKey.tokenLimit || limit || 0, + tokenLimit: newKey.tokenLimit || 0, used: 0, createdAt: newKey.createdAt, isActive: true, @@ -616,8 +623,8 @@ router.put('/user/api-keys/:keyId', authenticateUser, async (req, res) => { }) } - // 限制用户只能修改特定字段 - const allowedFields = ['name', 'description', 'isActive'] + // 限制用户只能修改特定字段(不允许修改name) + const allowedFields = ['description', 'isActive'] const filteredUpdates = {} for (const [key, value] of Object.entries(updates)) { if (allowedFields.includes(key)) { diff --git a/src/services/ldapService.js b/src/services/ldapService.js index 1630cb72..5838f570 100644 --- a/src/services/ldapService.js +++ b/src/services/ldapService.js @@ -4,11 +4,22 @@ const logger = require('../utils/logger') class LDAPService { constructor() { this.client = null + + // 检查必需的LDAP配置 + if ( + !process.env.LDAP_URL || + !process.env.LDAP_BIND_DN || + !process.env.LDAP_BIND_PASSWORD || + !process.env.LDAP_BASE_DN + ) { + logger.warn('⚠️ LDAP配置不完整,请检查.env文件中的LDAP配置项') + } + this.config = { - url: process.env.LDAP_URL || 'ldap://172.25.3.100:389', - bindDN: process.env.LDAP_BIND_DN || 'LDAP-Proxy-Read', - bindPassword: process.env.LDAP_BIND_PASSWORD || 'Y%77JsVK8W', - baseDN: process.env.LDAP_BASE_DN || 'OU=微店,DC=corp,DC=weidian-inc,DC=com', + url: process.env.LDAP_URL || '', + bindDN: process.env.LDAP_BIND_DN || '', + bindPassword: process.env.LDAP_BIND_PASSWORD || '', + baseDN: process.env.LDAP_BASE_DN || '', searchFilter: process.env.LDAP_SEARCH_FILTER || '(&(objectClass=user)(cn={username}))', timeout: parseInt(process.env.LDAP_TIMEOUT) || 10000, connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT) || 10000 diff --git a/test-fixed-auto-link.js b/test-fixed-auto-link.js deleted file mode 100644 index e13b8ac1..00000000 --- a/test-fixed-auto-link.js +++ /dev/null @@ -1,54 +0,0 @@ -const jwt = require('jsonwebtoken'); -const config = require('./config/config'); -const fetch = require('node-fetch'); - -// 模拟创建一个包含displayName的JWT token -const userInfo = { - type: 'ad_user', - username: 'zhangji', - displayName: '张佶', - email: 'zhangji@weidian.com', - groups: ['CN=Weidian-IT组,OU=Weidian Groups,OU=微店,DC=corp,DC=weidian-inc,DC=com'], - loginTime: new Date().toISOString() -}; - -const token = jwt.sign(userInfo, config.security.jwtSecret, { - expiresIn: '8h' -}); - -console.log('测试修正后的自动关联功能'); -console.log('用户displayName: 张佶'); - -async function testFixedAutoLink() { - try { - console.log('\n=== 测试获取用户API Keys ==='); - - const response = await fetch('http://localhost:3000/admin/ldap/user/api-keys', { - headers: { - 'Authorization': `Bearer ${token}` - } - }); - - const result = await response.json(); - - console.log('\n结果:', JSON.stringify(result, null, 2)); - - if (result.success && result.apiKeys && result.apiKeys.length > 0) { - console.log('\n✅ 成功!找到了关联的API Key:'); - result.apiKeys.forEach(key => { - console.log(`- ID: ${key.id}`); - console.log(`- Name: ${key.name}`); - console.log(`- Key: ${key.key.substring(0, 10)}...${key.key.substring(key.key.length-10)}`); - console.log(`- Limit: ${key.limit}`); - console.log(`- Status: ${key.status}`); - }); - } else { - console.log('\n❌ 没有找到关联的API Key'); - } - - } catch (error) { - console.error('测试错误:', error.message); - } -} - -testFixedAutoLink(); \ No newline at end of file diff --git a/web/admin-spa/src/components/user/UserApiKeysView.vue b/web/admin-spa/src/components/user/UserApiKeysView.vue index 4e029731..e4d6b90f 100644 --- a/web/admin-spa/src/components/user/UserApiKeysView.vue +++ b/web/admin-spa/src/components/user/UserApiKeysView.vue @@ -25,18 +25,13 @@

API Key 将用于访问 Claude Relay Service

-
-

- API Key 名称将自动设置为您的用户名 -

- + +
+

+ API Key 名称将自动设置为您的用户名 +

+

使用额度:无限制

+