Após a conclusão API para verificar a senha do AD Mais tarde,Então tente escrever a parte para alterar a senha,Mesmo que eu tenha tropeçado, finalmente terminei.。Desta vez vou compartilhar como escrever três pacotes diferentes.,E o título mencionará “Altere sua senha do AD” É porque descobri durante o processo de escrita,Alguns métodos requerem permissões de um administrador de domínio para executar,Portanto, o escopo deste artigo será limitado apenas ao uso da conta e senha do próprio usuário.,Você pode concluir a ação de alterar a senha。
"Modelo de Projeto"
- ASP .NET Core Web API
"Suíte"
De um modo geral,Pode haver três tipos de pacotes usados para modificar o AD::
- System.DirectoryServices.Protocols (SDSP)
- System.DirectoryServices (FDS)
- System.DirectoryServices.AccountManagement (S.DS.AM)
Comparação de três kits:
- nível operacional:S.DS.AM > S.DS > S.DS.P
- complexidade:S.DS.P > S.DS > S.DS.AM
- Flexibilidade:S.DS.P > S.DS > S.DS.AM
- velocidade:S.DS.P > S.DS > S.DS.AM
另外,Como S.DS.P é uma operação de baixo nível baseada no protocolo LDAP,Então a compatibilidade também é alta,Para OpenLDAP、O suporte AD não é problema,Mas S.DS e S.DS.AM podem ser mais limitados.,AD para Microsoft (Active Directory)。
Serviços/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; // 修改密碼 (透過 System.DirectoryServices.Protocols) public bool ChangePasswordSdsP(string username, string oldPassword, string newPassword) { try { using var connection = new LdapConnection(new LdapDirectoryIdentifier(_ldapServer, 636)); // 修改密碼必須使用 LDAPS 636 port。 connection.Credential = new NetworkCredential(username, oldPassword, _domain); connection.SessionOptions.SecureSocketLayer = true; // 修改密碼必須使用 LDAPS。 connection.AuthType = AuthType.Kerberos; // 如果使用 Negotiate 會先嘗試 Kerberos,失敗再改試 NTLM。 connection.Bind(); // 嘗試綁定,成功表示驗證通過 // 以 sAMAccountName 查詢使用者的 DN SearchRequest searchRequest = new( _baseDn, // 根目錄 $"(sAMAccountName={username})", // 根據 sAMAccountName 查詢 System.DirectoryServices.Protocols.SearchScope.Subtree, "distinguishedName" // 只獲取 DN 屬性 ); SearchResponse searchResponse = (SearchResponse)connection.SendRequest(searchRequest); if (searchResponse.Entries.Count == 0) { Console.WriteLine("User not found."); return false; } string userDn = searchResponse.Entries[0].DistinguishedName; // 使用 LDAP 修改密碼屬性 (unicodePwd) 時, // 只有高權限帳號 (如 Domain Admins) 可以對 unicodePwd 屬性執行修改 (DirectoryAttributeOperation.Replace)。 // 若要讓一般使用者更改自己的密碼,必須透過 LDAPS (安全通道),且同時送出 delete 與 add 操作來替換密碼。 var deleteOldPassword = new DirectoryAttributeModification { Operation = DirectoryAttributeOperation.Delete, Name = "unicodePwd" }; deleteOldPassword.Add(Encoding.Unicode.GetBytes($"\"{oldPassword}\"")); var addNewPassword = new DirectoryAttributeModification { Operation = DirectoryAttributeOperation.Add, Name = "unicodePwd" }; addNewPassword.Add(Encoding.Unicode.GetBytes($"\"{newPassword}\"")); // 組合 ModifyRequest,執行 Delete + Add 操作 var request = new ModifyRequest( userDn, deleteOldPassword, addNewPassword ); connection.SendRequest(request); return true; } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); return false; } } // 修改密碼 (透過 System.DirectoryServices) [SupportedOSPlatform("windows")] // 宣告以下方法僅適用於 Windows,避免 PrincipalContext 等 API 被提示要留意跨平臺問題。 public bool ChangePasswordSds(string username, string oldPassword, string newPassword) { try { // 使用 DirectorySearcher 以 sAMAccountName 查詢使用者的 DN DirectorySearcher searcher = new(new DirectoryEntry($"LDAP://{_baseDn}")); searcher.Filter = $"(sAMAccountName={username})"; searcher.PropertiesToLoad.Add("distinguishedName"); SearchResult result = searcher.FindOne(); if (result != null) { string userDn = result.Properties["distinguishedName"][0].ToString(); using DirectoryEntry user = new($"LDAP://{userDn}", username, oldPassword); user.Invoke("ChangePassword", [oldPassword, newPassword]); user.CommitChanges(); return true; } else { Console.WriteLine("User not found."); return false; } } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); return false; } } // 修改密碼 (透過 System.DirectoryServices.AccountManagement) [SupportedOSPlatform("windows")] // 宣告以下方法僅適用於 Windows,避免 PrincipalContext 等 API 被提示要留意跨平臺問題。 public bool ChangePasswordSdsAm(string username, string oldPassword, string newPassword) { try { using var context = new PrincipalContext(ContextType.Domain, _domain); // 驗證舊密碼是否正確 if (!context.ValidateCredentials(username, oldPassword)) { throw new UnauthorizedAccessException("用戶名或舊密碼不正確!"); } // 使用 UserPrincipal 修改密碼 using var user = UserPrincipal.FindByIdentity(context, username) ?? throw new Exception("找不到指定的使用者!"); user.ChangePassword(oldPassword, newPassword); return true; } catch (Exception ex) { Console.WriteLine($"修改密碼時發生錯誤: {ex.Message}"); return false; } } } }
Controladores/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 { // 修改密碼 (透過 System.DirectoryServices.Protocols) [HttpPost("ChangeSdsP")] public IActionResult ChangeSdsP([FromBody] PasswordChangeRequest request) { if (passwordManagement.ChangePasswordSdsP(request.Username, request.OldPassword, request.NewPassword)) { return Ok("密碼修改成功。"); } return BadRequest("密碼修改失敗。"); } // 修改密碼 (透過 System.DirectoryServices) [HttpPost("ChangeSds")] [SupportedOSPlatform("windows")] // 宣告以下用到的方法僅適用於 Windows,避免 ChangePassword 方法被提示要留意跨平臺問題。 public IActionResult ChangeSds([FromBody] PasswordChangeRequest request) { if (passwordManagement.ChangePasswordSds(request.Username, request.OldPassword, request.NewPassword)) { return Ok("密碼修改成功。"); } return BadRequest("密碼修改失敗。"); } // 修改密碼 (透過 System.DirectoryServices.AccountManagement) [HttpPost("ChangeSdsAm")] [SupportedOSPlatform("windows")] // 宣告以下用到的方法僅適用於 Windows,避免 ChangePassword 方法被提示要留意跨平臺問題。 public IActionResult ChangeSdsAm([FromBody] PasswordChangeRequest request) { if (passwordManagement.ChangePasswordSdsAm(request.Username, request.OldPassword, request.NewPassword)) { return Ok("密碼修改成功。"); } return BadRequest("密碼修改失敗。"); } } }
Modelos/AuthRequests.cs
namespace AD.Models { public class PasswordChangeRequest { public string Username { get; set; } // 使用者帳戶 public string OldPassword { get; set; } // 舊密碼 public string NewPassword { get; set; } // 新密碼 } }
Modelos/LdapSettings.cs
namespace AD.Models { public class LdapSettings { public string Server { get; set; } = string.Empty; public string Domain { get; set; } = string.Empty; public string BaseDn { get; set; } = string.Empty; } }
appsettings.json
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "LdapSettings": { "Server": "dc1.abc.com.tw", // 如果是用 Kerberos 驗證,AD 的伺服器不可以使用 IP。 "Domain": "abc.com.tw", "BaseDn": "DC=abc,DC=com,DC=tw" } }
Programa.cs
using AD.Models; using AD.Services; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); builder.Services.Configure<LdapSettings>(builder.Configuration.GetSection("LdapSettings")); // 讀取 appsettings.json 的 LdapSettings 資料。 builder.Services.AddScoped<PasswordManagementService>(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); // Configure the HTTP request pipeline. // 讓 Swagger 只在開發環境時使用。 if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
Durante o teste,Recomenda-se usar Swagger,Será muito mais conveniente。
Fim,O servidor DC deve confirmar se suporta LDAPS,Além de 636 Para fazer ping,Também preciso ajudar o DC a instalar credenciais.,Será mais preciso usar a ferramenta ldp.exe no DC para verificar。另外,Lembre-se de confirmar se a opção “Os usuários não podem alterar as senhas” na conta de teste não está marcada.,Estou preso aqui há vários dias,Finalmente percebi que esqueci de desmarcar...。
"Link de Referência"
[…] 密碼驗證》與《AD 密碼修改》兩個 API 後,Em seguida, use .NET MAUI Blazor para escrever programas de desktop。稍微瞭解了 Blazor […]