現在網路上大多的文章都是 .NET Framework 的 Windows Active Directory 驗證教學和示範,很少 .NET Core 5 或是 .NET 6 以上的資訊,筆者在串接 AD 驗證的時候一直遇到問題,將此解決過程記錄下來,希望能夠幫助到有需要的人
以下正文:
先使用 NuGet 安裝 System.DirectoryServices.AccountManagement 套件,或是使用 .NET CLI 執行以下指令安裝
錯誤示範:
筆者在測試的時候一直使用下面的程式碼進行測試,一直拋出錯誤
錯誤訊息如下:
奇怪,明明在第 6 行的時候就是登入成功,那為什麼第 13 行會出現「使用者名稱或密碼不正確」呢?又再建了一個新帳號,怕實際不能登入,下載 Active Directory Explorer 做測試,確實可以登入,使用錯誤的密碼第 6 行也確實會失敗。離開公司時邊走路邊想,邊騎車邊想,邊吃晚餐邊想... 啊!為什麼第 13 行只需要帳號不需要密碼,該不會這裡說的是 context 的使用者名稱或密碼錯誤吧?!
看了一下文件,還真的有多載...
再不看文件阿...
調整後 context.UserName 也讀的到了, UserPrincipal 也有東西,終於可以睡覺了!
常用資料整理:
AD 使用者 - 一般
AD 使用者 - 組織
順便附上查看所有使用者屬性資料的方式:
參考資料:
Microsoft.Learn - PrincipalContext Constructors
Microsoft.Learn - UserPrincipal Class
以下正文:
先使用 NuGet 安裝 System.DirectoryServices.AccountManagement 套件,或是使用 .NET CLI 執行以下指令安裝
dotnet add package System.DirectoryServices.AccountManagement
錯誤示範:
筆者在測試的時候一直使用下面的程式碼進行測試,一直拋出錯誤
string domain = "192.168.0.100";
string username = "ruyut";
string password = "Password123";
using PrincipalContext context = new PrincipalContext(ContextType.Domain, domain);
bool exist = context.ValidateCredentials(username, password);
Console.WriteLine(exist ? "登入成功" : "登入失敗"); // 登入成功
Console.WriteLine($"contextName: {context.Name}"); // contextName: 192.168.0.100
Console.WriteLine($"context.UserName: {context.UserName}"); // context.UserName:
UserPrincipal user = UserPrincipal.FindByIdentity(context, username); // 拋出錯誤
錯誤訊息如下:
Unhandled exception. System.DirectoryServices.DirectoryServicesCOMException (0x8007052E): 使用者名稱或密碼不正確。
at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
at System.DirectoryServices.DirectoryEntry.Bind()
at System.DirectoryServices.DirectoryEntry.get_AdsObject()
at System.DirectoryServices.PropertyValueCollection.PopulateList()
at System.DirectoryServices.PropertyValueCollection..ctor(DirectoryEntry entry, String propertyName)
at System.DirectoryServices.PropertyCollection.get_Item(String propertyName)
at System.DirectoryServices.AccountManagement.PrincipalContext.DoLDAPDirectoryInitNoContainer()
at System.DirectoryServices.AccountManagement.PrincipalContext.DoDomainInit()
at System.DirectoryServices.AccountManagement.PrincipalContext.Initialize()
at System.DirectoryServices.AccountManagement.PrincipalContext.get_QueryCtx()
at System.DirectoryServices.AccountManagement.Principal.FindByIdentityWithTypeHelper(PrincipalContext context, Type principalType, Nullable`1 identityType, String identityValue, DateTime refDate)
at System.DirectoryServices.AccountManagement.Principal.FindByIdentityWithType(PrincipalContext context, Type principalType, String identityValue)
at System.DirectoryServices.AccountManagement.UserPrincipal.FindByIdentity(PrincipalContext context, String identityValue)
at Program.<Main>$(String[] args) in C:\Users\ruyut\Documents\RiderProjects\2022\Test\ConsoleAppAdTest\ConsoleAppAdTest\Program.cs:line 13
奇怪,明明在第 6 行的時候就是登入成功,那為什麼第 13 行會出現「使用者名稱或密碼不正確」呢?又再建了一個新帳號,怕實際不能登入,下載 Active Directory Explorer 做測試,確實可以登入,使用錯誤的密碼第 6 行也確實會失敗。離開公司時邊走路邊想,邊騎車邊想,邊吃晚餐邊想... 啊!為什麼第 13 行只需要帳號不需要密碼,該不會這裡說的是 context 的使用者名稱或密碼錯誤吧?!
看了一下文件,還真的有多載...
再不看文件阿...
調整後 context.UserName 也讀的到了, UserPrincipal 也有東西,終於可以睡覺了!
string domain = "192.168.0.100";
string username = "ruyut";
string password = "Password123";
using PrincipalContext context = new PrincipalContext(ContextType.Domain, domain, username, password);
bool exist = context.ValidateCredentials(username, password);
Console.WriteLine(exist ? "登入成功" : "登入失敗"); // 登入成功
Console.WriteLine($"contextName: {context.Name}"); // contextName: 192.168.0.100
Console.WriteLine($"context.UserName: {context.UserName}"); // context.UserName: ruyut@ruyut.com
UserPrincipal user = UserPrincipal.FindByIdentity(context, username);
常用資料整理:
Console.WriteLine($"使用者名稱: {user.Name}"); // 使用者名稱: R
Console.WriteLine($"使用者顯示名稱: {user.DisplayName}"); // 使用者顯示名稱: Ruyut
Console.WriteLine($"描述: {user.Description}"); // 描述: 測試描述
Console.WriteLine($"Email: {user.EmailAddress}"); // Email: 測試電子郵件
DirectoryEntry? directoryEntry = user.GetUnderlyingObject() as DirectoryEntry;
Console.WriteLine($"公司: {directoryEntry?.Properties["company"].Value}"); // 公司: 測試公司
Console.WriteLine($"部門: {directoryEntry?.Properties["department"].Value}"); // 部門: 測試部門
Console.WriteLine($"職稱: {directoryEntry?.Properties["title"].Value}"); // 職稱: 測試職稱
Console.WriteLine($"電話號碼: {directoryEntry?.Properties["telephonenumber"].Value}"); // 電話號碼: 測試電話號碼
Console.WriteLine($"辦公室: {directoryEntry?.Properties["physicaldeliveryofficename"].Value}"); // 辦公室: 測試辦公室
Console.WriteLine($"網頁: {directoryEntry?.Properties["wwwhomepage"].Value}"); // 網頁: 測試網頁
AD 使用者 - 一般
AD 使用者 - 組織
順便附上查看所有使用者屬性資料的方式:
var searcher = new DirectorySearcher(user.GetUnderlyingObject() as DirectoryEntry);
foreach (SearchResult result in searcher.FindAll())
{
foreach (string propertyName in result.Properties.PropertyNames)
{
foreach (object propertyValue in result.Properties[propertyName])
{
Console.WriteLine($"{propertyName} : {propertyValue}");
}
}
}
參考資料:
Microsoft.Learn - PrincipalContext Constructors
Microsoft.Learn - UserPrincipal Class
留言
張貼留言
如果有任何問題、建議、想說的話或文章題目推薦,都歡迎留言或來信: a@ruyut.com