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

在Qt中验证LDAP账户(Windows平台)

一、前言

  原本以为在Qt(Windows平台)中验证 LDAP 账户很简单:集成Open LDAP的开发库即可。结果临了才发现,Open LDAP压根儿不支持Windows平台。沿着重用的原则,考虑迁移Open LDAP的源代码,却发现工作量不小:特别是 socket 部分。
  至此,也顾不上重用、跨平台了,先把 Windows 平台的搞定再说。结果发现 Windows 原本就提供了对 LDAP 的支持:Wldap32,且支持C/C++语言。
  相关参考:https://learn.microsoft.com/zh-cn/previous-versions/windows/desktop/ldap/lightweight-directory-access-protocol-ldap-api。

二、实现过程

(1)引入Wldap32库(在.pro文件中):

LIBS += -lwldap32

(2)包含相关头文件:

#include <windows.h>
#include <winldap.h>
//必须保证 winldap.h 在 winber.h 前面包含
#include <winber.h>

(3)账户(uid:password)验证过程:

  步骤  函数  依赖
1.初始化会话ldap_init主机,端口
2.设置会话选项ldap_set_option设置为3.0版本
3.连接到LDAP服务器ldap_connect可设置连接超时
4.绑定到服务器ldap_bind_s绑定用DN,绑定用密码
5.按账户uid搜索条目并解析得到密码hashldap_search_s
ldap_first_entry
ldap_first_attribute
ldap_get_values
基础DN,筛选表达式
6.验证密码SSHA解码/编码LDAP中存储的密码hash,账户密码

  其中主机、端口、绑定用DN、基础DN、搜索筛选表达式、uid等内容和规格参见实例图。

(4)实例图:
LDAP客户实例

三、关键代码

  附后。

四、后语(问题&总结)

(1)QString 与宽字符串的相互转换
  QString 的方法 toStdWString() 用于将 QString 转换成宽字符串。
  QString 的方法 fromStdWString() 用于将宽字符串转换成 QString。

(2)SSHA解密/加密步骤:
  ① 从 LDAP 所保存的 hash 中抽出 Salt;
  ② 使用①得到的 Salt 和需验证的密码生成 SHA-1 的 hash 值;
  ③ 比较②得到的 hash 值与 LDAP 中的hash值。

附录

(1)代码片段:绑定到服务器

int LdapUtil::bind(LDAP *pSession, const QString &bindDNStr, const QString &credStr)
{ULONG retCode = LDAP_SUCCESS;//认证信息std::wstring wBindDnStr = bindDNStr.toStdWString();std::wstring wCredStr = credStr.toStdWString();PWSTR bindDN = (PWSTR) wBindDnStr.c_str();PWSTR cred = (PWSTR) wCredStr.c_str();ULONG method = LDAP_AUTH_SIMPLE; //识别模式//向LDAP服务器认证客户端retCode = ldap_bind_s(pSession, bindDN, cred, method);if (retCode != LDAP_SUCCESS) {qDebug() << "Invoke ldap_bind_s fail, error code =" << retCode;}return retCode;
}

(2)代码片段:按uid搜索条目并解析得到密码(hash)

QString LdapUtil::getUserPassword(LDAP *pSession,const QString &baseDNStr,const QString &uid,int &retCode)
{QString templ = "(&(objectClass=person)(objectClass=organizationalPerson)(uid=%1))";std::wstring wBaseDN = baseDNStr.toStdWString();PWSTR baseDN = (PWSTR) wBaseDN.c_str();const QString filterStr = templStr.replace("%1", uid);std::wstring wFilter = filterStr.toStdWString();PWSTR filter = (PWSTR) wFilter.c_str();//查询密码PWCHAR attrs[2];attrs[0] = (PWCHAR) L"userPassword";attrs[1] = NULL;retCode = LDAP_SUCCESS;LDAPMessage *pSearchResult = NULL;QString ret;retCode = ldap_search_s(pSession, baseDN, LDAP_SCOPE_SUBTREE, filter, attrs, 0,&pSearchResult);if (retCode != LDAP_SUCCESS) {qDebug() << "Invoke ldap_search_s fail, error code ="<< QString::fromStdWString(ldap_err2string(retCode));if (pSearchResult != NULL)ldap_msgfree(pSearchResult);return ret;}ULONG numberOfEntries = ldap_count_entries(pSession, pSearchResult);if (numberOfEntries < 1) { //检索到的条目为空retCode = -1;return ret;}LDAPMessage *pEntry = NULL;pEntry = ldap_first_entry(pSession, pSearchResult);if (pEntry == NULL) { //Not found any entryldap_msgfree(pSearchResult);retCode = -1;return ret;}BerElement *pBer = NULL;PWCHAR pAttribute = NULL;// Get the first attribute name.pAttribute = ldap_first_attribute(pSession, // Session handlepEntry,   // Current entry&pBer);   // [out] Current BerElementif (pBer != NULL) {ber_free(pBer, 0);pBer = NULL;}if (pAttribute == NULL) { //Not found the attributeldap_msgfree(pSearchResult);retCode = -1;return ret;}// Get the string values.PWCHAR *ppValue = NULL;ppValue = ldap_get_values(pSession,    // Session HandlepEntry,      // Current entrypAttribute); // Current attributeif (ppValue == NULL) { //Get attribute's value failqDebug() << ": [NO ATTRIBUTE VALUE RETURNED]";ldap_msgfree(pSearchResult);retCode = -1;return ret;}// Output the attribute valuesULONG iValue = 0;iValue = ldap_count_values(ppValue);if (!iValue) {qDebug() << ": [BAD VALUE LIST]";// Free memory.if (ppValue != NULL)ldap_value_free(ppValue);ppValue = NULL;ldap_memfree(pAttribute);ldap_msgfree(pSearchResult);retCode = -1;return ret;}// Output the first attribute valueret = QString::fromStdWString(*ppValue);// Free memory.if (ppValue != NULL) {ldap_value_free(ppValue);}ppValue = NULL;ldap_memfree(pAttribute);ldap_msgfree(pSearchResult);retCode = LDAP_SUCCESS;return ret;
}

(3)代码片段:校验Salted SHA密码

bool LdapUtil::verifySaltSha(const QString &pass, const QString &storedHash)
{QString ldapPass;if (storedHash.startsWith("{SSHA}")) {ldapPass = storedHash.mid(6);} else if (storedHash.startsWith("{SHA}")) {ldapPass = storedHash.mid(5);}//按base64解密QByteArray decodedData = QByteArray::fromBase64(ldapPass.toUtf8());//从密文中获取SaltQByteArray salt = __getSalt(decodedData);//再加密明文(得到密文为base64加密)QByteArray newHash = sshaEncrypt(pass, salt);return (QByteArray::fromBase64(newHash) == decodedData);
}

【完】

相关文章:

  • 【dataframe显示不全问题】打开一个行列超多的excel转成df之后行列显示不全
  • Android tinyalsa库函数剖析
  • 几款开源C#插件框架
  • 2025年山东燃气瓶装送气工考试真题练习
  • 单调队列模板cpp
  • Java漏洞原理与实战
  • RT-DETR源码学习bug记录
  • 51单片机实验七:EEPROM AT24C02 与单片机的通信实例
  • 【系统架构设计师】统一过程模型(RUP)
  • python 对接支付宝账单流程及问题处理
  • 告别Feign:基于Spring 6.1 RestClient构建高可用声明式HTTP客户端
  • VUE快速入门-4:简单入门案例
  • postman使用设置
  • ChatGPT-o3辅助学术写作的关键词和引言效果如何?
  • 解锁古籍中的气候密码,探索GPT/BERT在历史灾害研究中的前沿应用;气候史 文本挖掘 防灾减灾;台风案例、干旱案例、暴雨案例
  • 面试面试面试new
  • docker 安装prometheus普罗米修斯
  • 疑难问题解决(2)
  • git常用的命令
  • CSS 美化页面(五)
  • 【社论】家政服务提质扩容,为何被一提再提
  • 男子拍摄女性视频后在网上配发诱导他人违法犯罪文字,已被警方行拘
  • 马上评丨“化学麻将”创新值得点赞,但要慎言推广
  • 自然资源部一季度新批用海项目中,涉历史遗留围填海项目56个
  • 西北政法大学推无手机课堂,有学生称要求全交,学校:并非强制
  • 中马签署互免签证协定,外交部:将进一步促进双边人员往来和交流合作