在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);
}

【完】

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/76283.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/76283.shtml
英文地址,請注明出處:http://en.pswp.cn/web/76283.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

《軟件設計師》復習筆記(11.4)——處理流程設計、系統設計、人機界面設計

目錄 一、業務流程建模 二、流程設計工具 三、業務流程重組&#xff08;BPR&#xff09; 四、業務流程管理&#xff08;BPM&#xff09; 真題示例&#xff1a; 五、系統設計 1. 主要目的 2. 設計方法 3. 主要內容 4. 設計原則 真題示例&#xff1a; 六、人機界面設…

UniRig ,清華聯合 VAST 開源的通用自動骨骼綁定框架

UniRig是清華大學計算機系與VAST聯合開發的前沿自動骨骼綁定框架&#xff0c;專為處理復雜且多樣化的3D模型而設計。基于強大的自回歸模型和骨骼點交叉注意力機制&#xff0c;UniRig能夠生成高質量的骨骼結構和精確的蒙皮權重&#xff0c;大幅提升動畫制作的效率和質量。 UniR…

LeetCode 443 壓縮字符串

字符數組壓縮算法詳解&#xff1a;實現與分析 一、引言 在處理字符數組時&#xff0c;我們常常遇到需要對連續重復字符進行壓縮的場景。這不僅可以節省存儲空間&#xff0c;還能提升數據傳輸效率。本文將深入解析一個經典的字符數組壓縮算法&#xff0c;通過詳細的實現步驟和…

alertManager部署安裝、告警規則配置詳解及告警消息推送

? java接受告警請求RestController RequestMapping("/alert") Slf4j public class TestApi {private static final DateTimeFormatter FORMATTER DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");RequestMappingpublic void sendTemplate(HttpServl…

數據庫勒索病毒威脅升級:企業數據安全防線如何用安當RDM組件重構

摘要&#xff1a;2025年Q1全球數據庫勒索攻擊量同比激增101.8%&#xff0c;Cl0p、Akira等團伙通過邊緣設備漏洞滲透企業核心系統&#xff0c;制造業、金融業等關鍵領域面臨數據加密與業務停擺雙重危機。本文深度解析勒索病毒對數據庫的五大毀滅性影響&#xff0c;結合安當RDM防…

thanos sidecar和receive區別?

Thanos Sidecar 和 Thanos Receive 是 Thanos 生態系統中兩個關鍵組件&#xff0c;但它們在架構中的作用和功能上有明顯的區別。以下是它們的主要區別&#xff1a; 1. Thanos Sidecar 功能&#xff1a; 與 Prometheus 集成&#xff1a; Sidecar 是一個部署在每個 Prometheus…

Unity入門筆記(緣更)

內容來源SiKi學院的Luna’s Fantasy 文章目錄 一、基礎知識1.準備2.基礎知識1.層級(Layer)2.軸心點3.預制體(Prefab)4.剛體組件(Rigidbody)5.碰撞器組件(BoxCollider) 二、代碼1.移動 一、基礎知識 1.準備 Unity安裝&#xff1a; https://unity.cn 2.基礎知識 1.層級(Layer…

使用VHD虛擬磁盤安裝雙系統,避免磁盤分區

前言 很多時候&#xff0c;我們對現在的操作系統不滿意,就想要自己安裝一個雙系統 但是安裝雙系統又涉及到硬盤分區,非常復雜,容易造成數據問題 虛擬機的話有經常用的不爽,這里其實有一個介于虛擬機和雙系統之間的解決方法,就是使用虛擬硬盤文件安裝系統. 相當于系統在機上…

ARINC818協議(五)

1.R_CTL,設置固定的0x44即可 2.Dest_ID:目的地D_ID,如果不需要目的地址&#xff0c;就設置為0&#xff1b;ADVB協議支持 多個視頻目的地址&#xff0c;廣播通信; 3.cs_ctl在FC-AV上不用 4.source_ID:S_ID [23:0]包含源實體的端口的地址標識&#xff1b;不用就設置為0. ADVB允許…

鴻蒙開發對于RelativeContainer高度設置‘auto‘后還是沒有自適應問題的解決方案

RelativeContainer設置高度為自適應‘auto’沒用生效&#xff0c;查看了官方文檔(文檔中心)也沒用給出明確的答案。只說了不能把錨點設置成父組件錨點&#xff08;__container__&#xff09;。也嘗試了使用guidline來替換父組件錨點&#xff0c;還是沒能自適應高度。 后來嘗試讓…

k8s教程3:Kubernetes應用的部署和管理

學習目標 理解Kubernetes中應用部署的基本概念和方法掌握Deployment、ReplicaSet、StatefulSet、DaemonSet、Job與CronJob等控制器的使用了解Helm作為Kubernetes的包管理工具的基本使用通過實際示例學習應用的部署、更新與管理 Kubernetes提供了一套強大而靈活的機制&#xff…

通過特定協議拉起 electron 應用

在 Android 通過 sheme 協議可以拉起其他應用。 electron 應用也可以通過類似特定協議被拉起。 在同時有 web、客戶端的應用里&#xff0c;可以通過這種方式在 web 拉起客戶端。 支持拉起客戶端 const PROTOCOL xxxif (process.defaultApp) {// 這里是開發環境&#xff0c;有…

算法備案的審核標準是什么?

隨著《互聯網信息服務算法推薦管理規定》等法規的出臺&#xff0c;算法備案成為了強制性備案&#xff0c;是產品合規上線的必要條件之一。本篇內容將從企業視角出發&#xff0c;分析算法備案的常見問題&#xff0c;意在對有備案需求的小伙伴們有所幫助。 一、誰需要做算法備案…

回顧與動機 - 為什么我們需要 Transformer

在接下來的旅程中,我們將一起探索深度學習領域最重要、最具影響力的模型架構之一——Transformer。從它的基本原理出發,逐步深入,最終能夠親手實現一個文本生成模型。 本系列教程假設你已經具備一定的深度學習基礎,了解神經網絡、損失函數、優化器等基本概念,并且熟悉 Py…

探索 Higress:下一代云原生 API 網關

引言 在云原生時代&#xff0c;API 網關作為連接客戶端與后端服務的橋梁&#xff0c;扮演著至關重要的角色。Higress 是一款由阿里巴巴開發的先進云原生 API 網關&#xff0c;基于開源的 Istio 和 Envoy 構建。它通過將流量網關、微服務網關和安全網關三者高度集成&#xff0c…

Spring Boot 整合 DeepSeek 實現AI對話 (保姆及教程)

文章目錄 文章目錄 前言 一、創建 spring boot 工程 二、申請key 三、修改配置文件 application.properties 四、編寫控制器&#xff08;controller&#xff09; 五、運行調試 前言 提示&#xff1a;隨著人工智能的不斷發展&#xff0c;ai這門技術也越來越重要&#xff0c;很多…

前端資源加載失敗后重試加載(CSS,JS等引用資源)

前端資源加載失敗后的重試 .前端引用資源時出現了資源加載失敗(這里針對的是路徑引用異常或者url解析錯誤時) 解決這個問題首先要明確一下幾個步驟 1.什么情況或者什么時候重試 2.如何重試 3.重試過程中的邊界處理 這里引入里三個測試腳本&#xff0c;分別加載里三個不同的腳…

無刷電機槽數相同、轉子極數不同的核心區別

一、基礎原理差異 無刷電機的核心參數: 槽數(定子槽數,記為 ( Z )):定子鐵芯上的繞組槽數量,決定繞組布局。極數(轉子磁極數,記為 ( 2p )):轉子上的永磁體磁極對數(總極數為 ( 2p ),如 ( p=4 ) 表示 8 極)。核心關系:槽極配合(( Z/2p ))決定電機電磁結構,相同…

6.Rust+Axum:打造高效 WebSocket 實時通信聊天室

摘要 本文詳細介紹 RustAxum 在 WebSocket 實時通信開發中的應用&#xff0c;包括雙向通信、狀態管理等&#xff0c;實踐構建聊天室應用。 一、引言 在當今的 Web 應用開發中&#xff0c;實時通信變得越來越重要。WebSocket 作為一種在單個 TCP 連接上進行全雙工通信的協議&…

clickhouse數據導出導入

clickhouse數據導出導入 CSV格式導出為csv格式導入為csv格式 JSON格式導出為json格式導入為json格式 SQL格式導出為SQL CSV格式 導出為csv格式 # 不帶表頭 clickhouse-client -h 127.0.0.1 --database"db" --query"select * from db.test_table FORMAT CSV&qu…