当前位置: 首页 > news >正文

ToB项目身份认证AD集成(完):利用ldap.js实现与windows AD对接实现用户搜索、认证、密码修改等功能 - 以及针对中文转义问题的补丁方法介绍

在前面的两篇文章中,我详细的介绍了使用ldap与window AD服务集成,实现ToB项目中的身份认证集成方案,包括技术方案介绍、环境配置:
ToB项目身份认证AD集成(一):基于目录的用户管理、LDAP和Active Directory简述
ToB项目身份认证AD集成(二):一分钟搞定window server 2003部署AD域服务并支持ssl加密(多图保姆教程+证书脚本)

在本文中,我将详细介绍如何利用 ldapjs 库使之一个 Node.js 服务类 LdapService,该类实现了与 之前搭建的Windows AD 交互,包括用户搜索、身份验证、密码修改等功能。

也算是AD集成系列的完结吧,后续可能出其它客户端的对接,但目前工作核心在AI那块儿,大概率也不会继续了

一、实现方案和LdapService类概述

LdapService 类的核心是通过 LDAP(轻量级目录访问协议)与 AD 进行交互,提供用户搜索、认证、密码修改、重置等功能。下图是该类的基本结构,后续将一步步的介绍如何实现各个方法。

class LdapService {client: Promise<ldap.Client>;private config: MustProperty<LdapServiceConfig>;constructor(config: LdapServiceConfig) {this.config = {...defaultConfig,...config,};this.client = this.init();}async findUsers(filter = this.config.userSearchFilter,attributes: string[] = ["sAMAccountName", "userPrincipalName", "memberOf"]) {}// 关闭连接async close() {(await this.client).destroy();}async findUser() {}// 修改用户密码的方法async changePassword(user: LdapUserSimInfo,newPassword: string,oldPassword: string) {}// 用户认证的方法 - 检查密码是否正确async checkPassword(user: LdapUserSimInfo, password: string) {}/*重置密码 */async resetPassword(user: LdapUserSimInfo, resetPassword: string) {}private async init() {const conf = this.config;const client = ldap.createClient({url: conf.url,tlsOptions: {minVersion: "TLSv1.2",rejectUnauthorized: false,},});await promisify(client.bind).call(client, conf.adminDN, conf.adminPassword);return client; // 返回绑定后的客户端}private mergeSearchEntryObjectAttrs(entry: ldap.SearchEntryObject) {}private doSearch(client: ldap.Client, opts: ldap.SearchOptions) {}private encodePassword(password) {}private safeDn(dn: string) {}
}

二、中文字段的特殊patch

ldap.js对于数据的字段进行了escape操作,会导致中文输入被转化成\xxx的形式,无论是接收的数据还是发送的请求,这时候会导致cn包含中文会出现错。需要用如下方法进行patch,通过在出现问题的rdn上配置unescaped参数控制是否对字符串进行escape(如果不知道啥是escape,参见十六进制转义escape介绍

const oldString = ldap.RDN.prototype.toString;
ldap.RDN.prototype.toString = function () {return oldString.call(this, { unescaped: this.unescaped });
};

加了这个补丁后,就可以控制rdn的转义情况了。

三、用户搜索功能

findUsers() 方法用于在 AD 中搜索用户,返回用户的基本信息。

async findUsers(filter = this.config.userSearchFilter,attributes: string[] = ["sAMAccountName", "userPrincipalName", "memberOf"]
): Promise<LdapUserSimInfo[]> {await this.bindAsAdmin();const opts = {filter, scope: "sub", attributes: Array.from(new Set(["distinguishedName", "cn"].concat(attributes))),};const searchResult = await this.doSearch(await this.client, opts);return searchResult.map((user) => {return this.mergeSearchEntryObjectAttrs(user) as LdapUserSimInfo;});
}
  • filter 是用于搜索的 LDAP 过滤器,默认为查找所有用户的 (objectClass=user) 过滤器。
  • attributes 参数允许指定返回哪些用户属性,默认返回 sAMAccountNameuserPrincipalNamememberOf 等属性。
  • 该方法调用了 doSearch() 进行搜索,并通过 mergeSearchEntryObjectAttrs() 整理和转换 AD 返回的用户数据。

doSearch() 方法是实际进行 LDAP 搜索的地方:

private doSearch(client: ldap.Client, opts: ldap.SearchOptions) {return new Promise<ldap.SearchEntryObject[]>((resolve, reject) => {const entries = [] as ldap.SearchEntryObject[];client.search(this.config.userSearchBase, opts, (err, res) => {if (err) {return reject(err);}res.on("searchEntry", (entry) => {entries.push(entry.pojo);});res.on("end", (result) => {if (result?.status !== 0) {return reject(new Error(`Non-zero status from LDAP search: ${result?.status}`));}resolve(entries);});res.on("error", (err) => {reject(err);});});});
}
  • client.search()ldapjs 提供的一个方法,用于执行搜索操作。搜索结果通过事件 searchEntry 逐条返回,最终在 end 事件时完成。

四、用户认证功能

checkPassword() 方法用于用户身份验证,检查用户输入的密码是否正确。

async checkPassword(user: LdapUserSimInfo, password: string) {const userDN = user.objectName;const client = await this.client;await promisify(client.bind).call(client, userDN, password);
}
  • 通过 LDAP 的 bind() 方法,可以尝试使用用户的 DN 和密码进行绑定。如果绑定成功,表示密码正确;否则,会抛出错误,表示认证失败。

五、密码修改功能

changePassword() 方法允许用户修改自己的密码。

async changePassword(user: LdapUserSimInfo, newPassword: string, oldPassword: string) {await this.bindAsAdmin();const userDN = this.safeDn(user.objectName);const changes = [new ldap.Change({operation: "delete",modification: new ldap.Attribute({type: "unicodePwd",values: [this.encodePassword(oldPassword)],}),}),new ldap.Change({operation: "add",modification: new ldap.Attribute({type: "unicodePwd",values: [this.encodePassword(newPassword)],}),}),];const client = await this.client;await promisify(client.modify).call(client, userDN, changes);
}
  • 在修改密码时,LDAP 需要先删除旧密码,再添加新密码。这里使用 ldap.Change 创建修改操作,通过 client.modify() 方法应用到 AD。

六、密码重置功能

resetPassword() 方法允许管理员重置用户的密码:

async resetPassword(user: LdapUserSimInfo, resetPassword: string) {await this.bindAsAdmin();const client = await this.client;const userDN = this.safeDn(user.objectName);const changes = new ldap.Change({operation: "replace",modification: new ldap.Attribute({type: "unicodePwd",values: [this.encodePassword(resetPassword)],}),});await promisify(client.modify).call(client, userDN, changes);
}
  • 与修改密码不同,重置密码直接使用 replace 操作,替换用户的现有密码。

七、结语

通过对 LdapService 类的逐步解析,相信你已经学会了如何利用 ldapjs 库与 Windows AD 进行交互。在实际使用中,还可以根据业务需求对这个类进行扩展,从而满足大规模企业系统中的用户管理需求。

另外这个中文的问题,暂时还只能是如此打补丁,期待社区修复可能不会那么及时

相关文章:

ToB项目身份认证AD集成(完):利用ldap.js实现与windows AD对接实现用户搜索、认证、密码修改等功能 - 以及针对中文转义问题的补丁方法介绍

在前面的两篇文章中&#xff0c;我详细的介绍了使用ldap与window AD服务集成&#xff0c;实现ToB项目中的身份认证集成方案&#xff0c;包括技术方案介绍、环境配置&#xff1a; ToB项目身份认证AD集成&#xff08;一&#xff09;&#xff1a;基于目录的用户管理、LDAP和Active…...

SpringBoot+SeetaFace6搭建人脸识别平台

前言 最近多个项目需要接入人脸识别功能&#xff0c;之前的方案是使用百度云api集成&#xff0c;但是后续部分项目是内网部署及使用&#xff0c;考虑到接入复杂程度及收费等多种因素&#xff0c;决定参考开源方案自己搭建&#xff0c;保证服务的稳定性与可靠性 项目地址&…...

MySQL-06.DDL-表结构操作-创建

一.DDL(表操作) create database db01;use db01;create table tb_user(id int comment ID&#xff0c;唯一标识,username varchar(20) comment 用户名,name varchar(10) comment 姓名,age int comment 年龄,gender char(1) comment 性别 ) comment 用户表; 此时并没有限制ID为…...

在Visual Studio中使用CMakeLists.txt集成EasyX库的详细指南

EasyX库是一款专为Windows平台设计的轻量级C图形库&#xff0c;适合初学者和教育领域使用。结合Visual Studio和CMake工具链&#xff0c;用户可以轻松创建C项目&#xff0c;并集成EasyX库&#xff0c;实现丰富的图形编程效果。本文将详细介绍如何在Visual Studio中通过CMakeLis…...

CRC码计算原理

CRC8这里先以CRC8来说明CRC的计算过程1、CRC8在线计算器通过CRC在线计算器可以看见CRC8的特征多项式:x8+x2+x+1,初始值为0000’0000。CRC计算的核心是:反转+异或+移位(此处的CRC8没有涉及反转,见后面CRC16)。2、CRC8计算过程(1)、取值从高到低依次取需校验数据的位,这里…...

对高危漏洞“Docker Engine API is accessible without authentication”的修复

一.背景 之前文章maven项目容器化运行之1-基于1Panel软件将docker镜像构建能力分享给局域网_1panel 构建镜像-CSDN博客将1Panel软件的Doocker端口给到了局域网&#xff0c;安全组兄弟扫描认为是高危漏洞&#xff0c;可能导致攻击者获取对Docker主机的完全控制权。 二.修复的建…...

两种方式创建Vue项目

文章目录 引言利用Vue命令创建Vue项目准备工作安装Vue CLI创建Vue项目方法一&#xff1a;使用vue init命令方法二&#xff1a;使用vue create命令启动Vue项目 利用Vite工具创建Vue项目概述利用Vite创建项目启动项目 结语 引言 大家好&#xff0c;今天我将向大家展示如何使用不…...

深入理解 C/C++ 指针

深入理解 C 指针&#xff1a;指针、解引用与指针变量的详细解析 前言 在 C 编程语言中&#xff0c;指针 是一个非常强大且重要的概念。对于初学者来说&#xff0c;指针往往会让人感到困惑不解。本文将通过形象的比喻&#xff0c;帮助大家深入理解指针、解引用与指针变量的概念…...

有什么方法可以保护ppt文件不被随意修改呢?

在工作或学习中&#xff0c;我们常常需要制作powerpoint演示文稿&#xff0c;担心自己不小心改动了或者不想他人随意更改&#xff0c;我们可以如何保护PPT呢&#xff1f;下面小编就来分享两个常用的方法。 方法一&#xff1a;为PPT设置打开密码 为PPT设置打开密码是最直接有效…...

[C#]项目中如何用 GraphQL 代替传统 WebAPI服务

在现代应用程序开发中&#xff0c;传统的 WebAPI 通常使用 RESTful 设计风格&#xff0c;然而近年来 GraphQL 作为一种新的 API 查询语言逐渐获得广泛应用。GraphQL 允许客户端精确地查询所需的数据&#xff0c;减少了过度请求和不足请求的问题。本文将详细讨论在项目中用 Grap…...

对后端返回的日期属性进行格式化(扩展 Spring MVC 的消息转换器)

格式化之前 格式化之后&#xff1a; 解决方式 方式一 在属性中加上注解&#xff0c;对日期进行格式化 JsonFormat(pattern "yyyy-MM-dd HH:mm:ss")private LocalDateTime createTime;//JsonFormat(pattern &quo…...

踩坑记录-用python解析php Laravel8生成的jwt token一直提示 Invalid audience

import jwtdef token_required(token):with open(storage/oauth-public.key, r) as f:public_key f.read()try:# 尝试使用当前算法解码 token&#xff0c;同时指定受众decoded jwt.decode(token, public_key, algorithms[RS256], options{"verify_aud": False})# p…...

使用IOT-Tree Server制作一个边缘计算设备(Arm Linux)

最近实现了一个小项目&#xff0c;现场有多个不同厂家的设备&#xff0c;用户需要对此进行简单的整合&#xff0c;并实现一些联动控制。 我使用了IOT-Tree Server这个软件轻松实现了&#xff0c;不外乎有如下过程&#xff1a; 1&#xff09;使用Modbus协议对接现有设备&#…...

(JAVA)B树和B+树的实现原理阐述

1. B 树 2-3树中&#xff0c;一个节点最多能有两个key&#xff0c;它的实现红黑树中适用对链接染色的方式去表达这两个key。下面将学习另一种树形结构B树&#xff0c;这种数据结构中&#xff0c;一个节点允许多余两个key的存在。 B树是一种树状数据结构&#xff0c;它能够存储…...

JC系列CAN通信说明

目录 一、CAN协议二、指令格式三、通信接线3.1、一对一通信3.2、组网通信 四、寄存器定义五、指令说明4、读取电源电压5、读取母线电流6、读取实时速度8、读取实时位置10、读取驱动器温度11、读取电机温度12、读取错误信息32、设定电流33、设定速度35、设定绝对位置37、设定相对…...

Ubuntu22——安装并配置局域网文件共享系统Samba

我们将共享目录设置为 /home/takway/share。以下是基于这个新目录的详细步骤&#xff1a; 在Ubuntu上安装并配置Samba 更新系统包列表 打开终端&#xff0c;执行以下命令来确保你的包列表是最新的&#xff1a; sudo apt update安装Samba 安装Samba及其相关工具&#xff1a; sud…...

HTML CSS 基础

HTML & CSS 基础 HTML一、HTML简介1、网页1.1 什么是网页1.2 什么是HTML1.3 网页的形成1.4总结 2、web标准2.1 为什么需要web标准2.2 Web 标准的构成 二、HTML 标签1、HTML 语法规范1.1基本语法概述1.2 标签关系 2、 HTML 基本结构标签2.1 第一个 HTML 网页2.2 基本结构标签…...

Nginx 使用 GeoIP 模块阻止特定国家 IP 地址的最佳实践

一、概述 为什么要阻止特定国家的 IP 地址&#xff1f; 在全球化的互联网上&#xff0c;网站和服务器可能会面对来自不同国家和地区的用户流量。虽然大多数情况下&#xff0c;我们希望网站能为全球用户提供服务&#xff0c;但在某些特定场景下&#xff0c;阻止来自特定国家的…...

vue3 + vite + cesium项目

GitHub - tingyuxuan2302/cesium-vue3-vite: 项目基于 vue3 vite cesium&#xff0c;已实现常见三维动画场&#xff0c;欢迎有兴趣的同学加入共建&#xff0c;官网服务器相对拉胯&#xff0c;请耐心等候...https://github.com/tingyuxuan2302/cesium-vue3-vite/tree/github...

DR模式 LVS负载均衡群集

DR模式 LVS负载均衡群集 部署共享存储关闭防火墙和核心防护下载&#xff0c;开启nfs服务创建共享文件夹和测试用的静态网页文件编辑nfs配置文件发布共享查看共享 配置 tomcat 服务器关闭防火墙和核心防护安装tomcat配置 tomcat 多实例 配置 nginx 服务器关闭防火墙和核心防护配…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

vulnyx Blogger writeup

信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面&#xff0c;gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress&#xff0c;说明目标所使用的cms是wordpress&#xff0c;访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

k8s从入门到放弃之HPA控制器

k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率&#xff08;或其他自定义指标&#xff09;来调整这些对象的规模&#xff0c;从而帮助应用程序在负…...

32单片机——基本定时器

STM32F103有众多的定时器&#xff0c;其中包括2个基本定时器&#xff08;TIM6和TIM7&#xff09;、4个通用定时器&#xff08;TIM2~TIM5&#xff09;、2个高级控制定时器&#xff08;TIM1和TIM8&#xff09;&#xff0c;这些定时器彼此完全独立&#xff0c;不共享任何资源 1、定…...