Files
claude-relay-service/src/services/userMappingService.js
iRubbish 7624c383e8 feat: 完整实现AD域控用户认证系统
主要功能:
- 新增LDAP服务连接AD域控服务器
- 实现多格式AD用户认证(sAMAccountName, UPN, 域\用户名, DN)
- 支持中文显示名和拼音用户名搜索
- 添加用户账户状态检查(禁用账户检测)
- 实现JWT token认证和用户会话管理

新增文件:
- src/services/ldapService.js - LDAP核心服务
- src/routes/ldapRoutes.js - AD认证API路由
- src/services/userMappingService.js - 用户映射服务
- web/admin-spa/src/views/UserDashboardView.vue - 用户控制台
- web/admin-spa/src/components/user/ - 用户组件目录

修改功能:
- ApiStatsView.vue 增加用户登录按钮和模态框
- 路由系统增加用户专用页面
- 安装ldapjs和jsonwebtoken依赖

技术特性:
- 多种认证格式自动尝试
- LDAP referral错误处理
- 详细认证日志和错误码记录
- 前后端完整用户认证流程

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 18:03:55 +08:00

196 lines
4.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const logger = require('../utils/logger')
/**
* 用户映射服务 - 处理AD用户数据转换和过滤
*/
class UserMappingService {
/**
* 解析AD用户账户控制状态
*/
static parseUserAccountControl(uac) {
if (!uac) {
return { disabled: true, description: 'Unknown' }
}
const uacValue = parseInt(uac)
const flags = {
SCRIPT: 0x00000001,
ACCOUNTDISABLE: 0x00000002,
HOMEDIR_REQUIRED: 0x00000008,
LOCKOUT: 0x00000010,
PASSWD_NOTREQD: 0x00000020,
PASSWD_CANT_CHANGE: 0x00000040,
ENCRYPTED_TEXT_PASSWORD_ALLOWED: 0x00000080,
TEMP_DUPLICATE_ACCOUNT: 0x00000100,
NORMAL_ACCOUNT: 0x00000200,
INTERDOMAIN_TRUST_ACCOUNT: 0x00000800,
WORKSTATION_TRUST_ACCOUNT: 0x00001000,
SERVER_TRUST_ACCOUNT: 0x00002000,
DONT_EXPIRE_PASSWD: 0x00010000,
MNS_LOGON_ACCOUNT: 0x00020000,
SMARTCARD_REQUIRED: 0x00040000,
TRUSTED_FOR_DELEGATION: 0x00080000,
NOT_DELEGATED: 0x00100000,
USE_DES_KEY_ONLY: 0x00200000,
DONT_REQUIRE_PREAUTH: 0x00400000,
PASSWORD_EXPIRED: 0x00800000,
TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION: 0x01000000,
PARTIAL_SECRETS_ACCOUNT: 0x04000000
}
const status = {
disabled: !!(uacValue & flags.ACCOUNTDISABLE),
locked: !!(uacValue & flags.LOCKOUT),
passwordExpired: !!(uacValue & flags.PASSWORD_EXPIRED),
normalAccount: !!(uacValue & flags.NORMAL_ACCOUNT),
passwordNotRequired: !!(uacValue & flags.PASSWD_NOTREQD),
dontExpirePassword: !!(uacValue & flags.DONT_EXPIRE_PASSWD),
description: this.getUserAccountControlDescription(uacValue)
}
return status
}
/**
* 获取用户账户控制的描述
*/
static getUserAccountControlDescription(uac) {
const uacValue = parseInt(uac)
if (uacValue & 0x00000002) {
return 'Account Disabled'
}
if (uacValue & 0x00000010) {
return 'Account Locked'
}
if (uacValue & 0x00800000) {
return 'Password Expired'
}
if (uacValue & 0x00000200) {
return 'Normal User Account'
}
return `UAC: ${uacValue}`
}
/**
* 过滤和映射AD用户数据
* 模拟Python代码中的get_ad()函数逻辑
*/
static mapAdUsers(searchResults) {
if (!Array.isArray(searchResults)) {
return []
}
// 移除第一个元素Python代码中的slist.pop(0)
const userList = searchResults.slice(1)
const mappedUsers = []
for (const user of userList) {
try {
const userObj = {
org: user.dn || user.distinguishedName,
cn: null,
userAccountControl: null,
accountStatus: null
}
// 提取CN
if (user.cn || user.CN) {
userObj.cn = user.cn || user.CN
} else {
// 如果没有CN属性跳过此用户
continue
}
// 提取userAccountControl
if (user.userAccountControl) {
userObj.userAccountControl = user.userAccountControl
userObj.accountStatus = this.parseUserAccountControl(user.userAccountControl)
} else {
// 如果没有userAccountControl跳过此用户
continue
}
mappedUsers.push(userObj)
} catch (error) {
logger.warn(`Error processing user entry: ${error.message}`, { user })
continue
}
}
return mappedUsers
}
/**
* 过滤活跃用户(未禁用的账户)
*/
static filterActiveUsers(users) {
return users.filter((user) => user.accountStatus && !user.accountStatus.disabled)
}
/**
* 根据用户名搜索(支持模糊匹配)
*/
static searchUsersByName(users, searchTerm) {
if (!searchTerm) {
return users
}
const term = searchTerm.toLowerCase()
return users.filter((user) => user.cn && user.cn.toLowerCase().includes(term))
}
/**
* 格式化用户信息用于显示
*/
static formatUserInfo(user) {
return {
name: user.cn,
distinguishedName: user.org,
accountControl: user.userAccountControl,
status: user.accountStatus
? {
enabled: !user.accountStatus.disabled,
locked: user.accountStatus.locked,
description: user.accountStatus.description
}
: null
}
}
/**
* 获取用户统计信息
*/
static getUserStats(users) {
const stats = {
total: users.length,
active: 0,
disabled: 0,
locked: 0,
passwordExpired: 0
}
users.forEach((user) => {
if (user.accountStatus) {
if (!user.accountStatus.disabled) {
stats.active++
}
if (user.accountStatus.disabled) {
stats.disabled++
}
if (user.accountStatus.locked) {
stats.locked++
}
if (user.accountStatus.passwordExpired) {
stats.passwordExpired++
}
}
})
return stats
}
}
module.exports = UserMappingService