延續之前兩個 AD 密碼相關的 API,接著寫了一個可以查詢 AD 使用者密碼即將到期的的 API,這篇就不再仔細寫出環境細節,包括 Models、appsettings.json、Program.cs 等,因為是延續之前的專案,如有需要可以參考前面的文章。
- 老森常譚 IT Help » 透過 ASP.NET Core 寫一個簡易的 AD 帳號密碼驗證 Web API
- 老森常譚 IT Help » 透過 ASP.NET Core 寫一個讓使用者可以修改自己 AD 密碼的 Web API
Services/PasswordManagementService.cs
using AD.Models;
using Microsoft.Extensions.Options;
using System.DirectoryServices;
using System.DirectoryServices.Protocols;
using System.Net;
using System.Text;
using System.DirectoryServices.AccountManagement;
using System.Runtime.Versioning;
namespace AD.Services
{
public class PasswordManagementService(IOptions<LdapSettings> ldapSettings)
{
private readonly string _ldapServer = ldapSettings.Value.Server;
private readonly string _domain = ldapSettings.Value.Domain;
private readonly string _baseDn = ldapSettings.Value.BaseDn;
// 查詢網域密碼政策
[SupportedOSPlatform("windows")] // 宣告以下方法僅適用於 Windows,避免 DirectorySearcher 等 class 被提示要留意跨平臺問題。
public TimeSpan GetMaxPasswordAge()
{
try
{
// 使用 DirectorySearcher 查詢網域《群組原則》的「密碼最長使用期限」。
using DirectorySearcher searcher = new(new DirectoryEntry($"LDAP://{_baseDn}"))
{
Filter = "(objectClass=domain)"
};
searcher.PropertiesToLoad.Add("maxPwdAge"); // 密碼最長使用期限
SearchResult? result = searcher.FindOne();
if (result != null && result.Properties.Contains("maxPwdAge"))
{
long maxPwdAgeTicks = (long)result.Properties["maxPwdAge"][0]; // 「密碼最長使用期限」原始數值,
return TimeSpan.FromTicks(maxPwdAgeTicks); // 轉成 TimeSpan 物件。(TimeSpan 物件在時間處理較為方便)
}
return TimeSpan.Zero; // 找不到資料時,回傳 0
}
catch (Exception ex)
{
Console.WriteLine($"查詢網域密碼政策時,發生錯誤: {ex.Message}");
return TimeSpan.Zero;
}
}
// 查詢密碼即將到期的使用者
[SupportedOSPlatform("windows")] // 宣告以下方法僅適用於 Windows,避免 GetMaxPasswordAge() 等方法被提示要留意跨平臺問題。
public List<String> GetUsersWithExpiringPasswords(int daysUntilExpiry)
{
List<String> expiringUsers = []; // 「密碼即將到期的使用者」清單。
try
{
TimeSpan MaxPasswordAge = GetMaxPasswordAge(); // 取得「密碼最長使用期限」。
if (MaxPasswordAge == TimeSpan.Zero) // 未設定「密碼最長使用期限」。
return expiringUsers;
DateTime today = DateTime.Now;
DateTime thresholdDate = today.AddDays(daysUntilExpiry); // XX 天 (daysUntilExpiry) 後的「到期日期」。
// 使用 DirectorySearcher 查詢網域《群組原則》的「密碼最長使用期限」。
using DirectorySearcher searcher = new(new DirectoryEntry($"LDAP://{_baseDn}"))
{
Filter = "(&(objectCategory=person)(objectClass=user)(pwdLastSet=*)(mail=*))" // 篩選有「上次密碼設定時間」、「Mail」的「使用者」。
};
searcher.PropertiesToLoad.Add("sAMAccountName"); // AD 帳號
searcher.PropertiesToLoad.Add("pwdLastSet"); // 上次密碼設定時間
searcher.PropertiesToLoad.Add("displayName"); // 顯示名稱
searcher.PropertiesToLoad.Add("mail"); // 電子郵件
foreach (SearchResult result in searcher.FindAll())
{
long pwdLastSetTicks = (long)result.Properties["pwdLastSet"][0]; // 使用者帳戶的「上次密碼設定時間」。
DateTime pwdLastSet = DateTime.FromFileTime(pwdLastSetTicks); // 轉成 DateTime 格式。(具體的日期)
DateTime passwordExpiryDate = pwdLastSet + MaxPasswordAge; // 使用者帳戶的「密碼到期日」。
if (passwordExpiryDate <= thresholdDate) // 已經到期或未來 XX 天內會到期的帳戶
{
string userSAMAccountName = result.Properties["sAMAccountName"][0].ToString() ?? string.Empty; // AD 帳號
string userPasswordExpiryDate = passwordExpiryDate.ToString("yyyy/MM/dd") ?? string.Empty; // 上次密碼設定時間
string userDisplayName = result.Properties["displayName"][0].ToString() ?? string.Empty; // 顯示名稱
string userMail = result.Properties["mail"][0].ToString() ?? string.Empty; // 電子郵件
string expiringUsersInfo = $"{userSAMAccountName};{userPasswordExpiryDate};{userDisplayName};{userMail}";
expiringUsers.Add(expiringUsersInfo);
}
}
return expiringUsers; // 若沒結果,回傳空的清單。
}
catch (Exception ex)
{
Console.WriteLine($"查詢密碼即將到期的使用者時,發生錯誤: {ex.Message}");
return expiringUsers;
}
}
}
}
Controllers/PasswordManagementController.cs
using AD.Models;
using AD.Services;
using Microsoft.AspNetCore.Mvc;
using System.Runtime.Versioning;
namespace AD.Controllers
{
[Route("password")]
[ApiController]
public class PasswordManagementController(PasswordManagementService passwordManagement) : ControllerBase
{
// 取得密碼即將到期的使用者
[HttpGet("get-expiry-users/{days}")]
[SupportedOSPlatform("windows")] // 宣告以下用到的方法僅適用於 Windows,避免 GetUsersWithExpiringPasswords 方法被提示要留意跨平臺問題。
public IActionResult GetExpiringUsers(int days)
{
try
{
var users = passwordManagement.GetUsersWithExpiringPasswords(days);
return Ok(new { expiringUsers = users }); // 回傳一個包含 ExpiringUsers 屬性,其值為 users 的 JSON 格式。
}
catch (Exception ex) {
return StatusCode(500, new { Message = "發生錯誤", Error = ex.Message });
}
}
}
}
《Swagger 測試結果》

《相關連結》
- 老森常譚 IT Help » 透過 ASP.NET Core 寫一個簡易的 AD 帳號密碼驗證 Web API
- 老森常譚 IT Help » 透過 ASP.NET Core 寫一個讓使用者可以修改自己 AD 密碼的 Web API








Leave a Reply