完成了《AD 密碼驗證》與《AD 密碼修改》兩個 API 後,接下來用 .NET MAUI Blazor 來寫桌面端的程式。稍微瞭解了 Blazor 後,覺得好神奇,感覺就是把網頁塞進應用程式裡面,運行時還可以在「工作管理員」看到背後有 Edge 的相關元件在運行,然後按 F12 還會有開發者工具視窗!不過這也讓我衍生了一點安全性的疑慮。但整體感覺還是挺特別的,剛好我對網頁語法比較熟,搭配 Razor 來設計 UI 畫面,感覺親切不少。
程式的結構是兩個頁面,第一個頁面是 AD 密碼驗證,通過後才會導到 AD 密碼變更頁面。
《專案範本》
- .NET MAUI Blazor 應用程式
《套件》
- bootstrap
Services/PasswordManagementService.cs
using System.Net.Http.Json; namespace NTool.Services { public class PasswordManagementService(HttpClient httpClient) { public async Task<bool> ValidateAsync(string username, string password) { var request = new { Username = username, Password = password }; var response = await httpClient.PostAsJsonAsync("Password/Validate", request); return response.IsSuccessStatusCode; } public async Task<bool> ChangeAsync(string username, string password, string newPassword) { var request = new { Username = username, OldPassword = password, NewPassword = newPassword }; var response = await httpClient.PostAsJsonAsync("Password/ChangeSdsP", request); return response.IsSuccessStatusCode; } } }
Components/Pages/PasswordValidation.razor
@page "/" @using NTool.Services @using NTool.Models @inject PasswordManagementService PasswordManagementService @inject ValidRequest ValidRequest @inject NavigationManager Navigation <h3 class="mt-5 text-center">AD 密碼變更工具</h3> <p>請驗證帳號密碼</p> <EditForm Model="ValidRequest" OnValidSubmit="HandleValidSubmit"> <DataAnnotationsValidator /> <ValidationSummary /> <div> <label for="username">使用者登入名稱</label> <InputText id="username" @bind-Value="ValidRequest.Username" placeholder="輸入帳號" /> </div> <div> <label for="password">密碼</label> <InputText id="password" @bind-Value="ValidRequest.Password" placeholder="輸入密碼" type="password" /> </div> <button type="submit">驗證</button> </EditForm> <p>@resultMessage</p> @code { private string resultMessage = string.Empty; private async Task HandleValidSubmit() { var isValid = await PasswordManagementService.ValidateAsync(ValidRequest.Username, ValidRequest.Password); if (isValid) { Navigation.NavigateTo("/PasswordChange"); } else { resultMessage = "驗證失敗。"; } } }
Components/Pages/PasswordChange.razor
@page "/PasswordChange" @using NTool.Models @using NTool.Services @inject PasswordManagementService PasswordManagementService @inject HttpClient Http @inject NavigationManager Navigation @inject ChangeRequest ChangeRequest @inject ValidRequest ValidRequest <h3 class="mt-5 text-center">AD 密碼變更工具</h3> <p>@Message</p> <EditForm Model="ChangeRequest" OnValidSubmit="HandleChangeSubmit"> <DataAnnotationsValidator /> <ValidationSummary /> <div> <label for="newPassword">新密碼</label> <InputText id="newPassword" @bind-Value="ChangeRequest.NewPassword" placeholder="輸入新密碼" type="password" /> </div> <div> <label for="confirmPassword">確認新密碼</label> <InputText id="confirmPassword" @bind-Value="ChangeRequest.ConfirmPassword" placeholder="確認新密碼" type="password" /> </div> <button type="submit">變更密碼</button> </EditForm> @code { private string Message { get; set; } = "驗證成功!請變更密碼"; private async Task HandleChangeSubmit() { var isValid = await PasswordManagementService.ChangeAsync(ValidRequest.Username, ValidRequest.Password, ChangeRequest.NewPassword); if (isValid) { Message = "密碼變更成功。"; ValidRequest.Password = string.Empty; ChangeRequest.NewPassword = string.Empty; ChangeRequest.ConfirmPassword = string.Empty; // Navigation.NavigateTo("/"); } else { Message = "密碼變更失敗。"; ChangeRequest.NewPassword = string.Empty; ChangeRequest.ConfirmPassword = string.Empty; } } }
Models/PasswordModel.cs
using System.ComponentModel.DataAnnotations; namespace NTool.Models { public class ValidRequest { [Required(ErrorMessage = "[使用者登入名稱] 為必填欄位。")] public required string Username { get; set; } [Required(ErrorMessage = "[密碼] 為必填欄位。")] public required string Password { get; set; } } public class ChangeRequest { [Required(ErrorMessage = "[新密碼] 為必填欄位。")] public required string NewPassword { get; set; } [Required(ErrorMessage = "[確認新密碼] 為必填欄位。")] [Compare("NewPassword", ErrorMessage = "[新密碼] 與 [確認新密碼] 不相符。")] public required string ConfirmPassword { get; set; } } }
MauiProgram.cs
using Microsoft.Extensions.Logging; using NTool.Services; using NTool.Models; namespace NTool { public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp<App>() .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); }); builder.Services.AddMauiBlazorWebView(); #if DEBUG builder.Services.AddBlazorWebViewDeveloperTools(); builder.Logging.AddDebug(); #endif // 注入 HttpClient,並設定 API 的基底網址 builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("https://api.abc.com/ad/") }); // 注入 Services、Models builder.Services.AddScoped<PasswordManagementService>(); builder.Services.AddScoped<ValidRequest>(); builder.Services.AddScoped<ChangeRequest>(); return builder.Build(); } } }
wwwroot/css/styles.css
body { background-color: #f0f8ff; /* 淡粉藍背景色 */ font-family: Arial, sans-serif; margin: 0; padding: 0; } form { max-width: 400px; margin: 50px auto; background: white; border-radius: 8px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); padding: 20px; } form div { margin-bottom: 15px; } form label { display: block; font-weight: bold; margin-bottom: 5px; } form input { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; } form button { width: 100%; padding: 10px; background-color: #4caf50; color: white; border: none; border-radius: 4px; font-size: 16px; cursor: pointer; } form button:hover { background-color: #45a049; } p { text-align: center; color: #ff0000; font-weight: bold; }