如何限制ASP.NET Core Identity仅开放登录注册?Blazor Wasm应用问题
实现方案:限制Identity功能并迁移账户管理/登录注册到Blazor UI
一、限制Identity仅开放Login和Register
1. 移除无关页面
直接删除Server项目中Areas/Identity/Pages/Account目录下除Login.cshtml、Register.cshtml之外的所有文件(如Manage文件夹、ForgotPassword.cshtml等),从根源上禁用账户管理相关页面的访问。
2. 路由权限加固
在Server项目的Program.cs中配置Razor Pages路由规则,确保仅允许匿名访问登录、注册页面,其他Identity路由需授权(或直接拦截):
builder.Services.Configure<RazorPagesOptions>(options => { // 允许匿名访问登录、注册 options.Conventions.AllowAnonymousToAreaPage("Identity", "/Account/Login"); options.Conventions.AllowAnonymousToAreaPage("Identity", "/Account/Register"); // 其他Account下的路由强制授权(无权限则无法访问) options.Conventions.AuthorizeAreaFolder("Identity", "/Account"); });
二、在Blazor Wasm端实现账户管理
账户管理依赖Server端API提供数据操作能力,客户端负责UI交互,步骤如下:
1. Server端编写账户管理API
创建AccountManagementController,注入UserManager<IdentityUser>实现核心逻辑:
[Authorize] [ApiController] [Route("api/account")] public class AccountManagementController : ControllerBase { private readonly UserManager<IdentityUser> _userManager; public AccountManagementController(UserManager<IdentityUser> userManager) { _userManager = userManager; } // 修改密码接口 [HttpPost("changepassword")] public async Task<IActionResult> ChangePassword(ChangePasswordRequest request) { var user = await _userManager.GetUserAsync(User); if (user == null) return NotFound("用户不存在"); var result = await _userManager.ChangePasswordAsync(user, request.OldPassword, request.NewPassword); if (!result.Succeeded) { foreach (var error in result.Errors) ModelState.AddModelError(string.Empty, error.Description); return BadRequest(ModelState); } return Ok("密码修改成功"); } // 获取当前用户信息接口 [HttpGet("profile")] public async Task<IActionResult> GetProfile() { var user = await _userManager.GetUserAsync(User); if (user == null) return NotFound(); return Ok(new { user.Email, user.UserName, user.PhoneNumber }); } // 其他接口:更新个人信息、绑定手机号等 } public class ChangePasswordRequest { [Required] public string OldPassword { get; set; } [Required, StringLength(100, MinimumLength = 6)] public string NewPassword { get; set; } [Compare("NewPassword")] public string ConfirmPassword { get; set; } }
2. Blazor端编写账户管理组件
以修改密码组件为例(ChangePassword.razor):
@page "/account/changepassword" @inject HttpClient Http @inject NavigationManager NavManager <h3>修改密码</h3> <EditForm Model="@request" OnValidSubmit="@HandleChangePassword"> <DataAnnotationsValidator /> <ValidationSummary /> <div class="mb-3"> <label class="form-label">旧密码</label> <InputPassword @bind-Value="request.OldPassword" class="form-control" /> </div> <div class="mb-3"> <label class="form-label">新密码</label> <InputPassword @bind-Value="request.NewPassword" class="form-control" /> </div> <div class="mb-3"> <label class="form-label">确认新密码</label> <InputPassword @bind-Value="request.ConfirmPassword" class="form-control" /> </div> <button type="submit" class="btn btn-primary">确认修改</button> </EditForm> @code { private ChangePasswordRequest request = new(); private async Task HandleChangePassword() { var response = await Http.PostAsJsonAsync("api/account/changepassword", request); if (response.IsSuccessStatusCode) { NavManager.NavigateTo("/account/profile"); } else { var errors = await response.Content.ReadFromJsonAsync<Dictionary<string, string[]>>(); // 处理并显示错误信息 } } private class ChangePasswordRequest { [Required] public string OldPassword { get; set; } [Required, StringLength(100, MinimumLength = 6)] public string NewPassword { get; set; } [Compare("NewPassword")] public string ConfirmPassword { get; set; } } }
3. 配置HttpClient认证拦截
确保Blazor端的HttpClient能自动携带JWT令牌,在Wasm项目的Program.cs中配置:
builder.Services.AddHttpClient("ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)) .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>(); builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("ServerAPI"));
三、用Blazor UI替代Identity的登录/注册
完全可以用Blazor组件实现登录注册,步骤如下:
1. 禁用Identity默认登录注册页面
删除Server项目中Areas/Identity/Pages/Account/Login.cshtml和Register.cshtml,或在路由配置中拦截访问。
2. Blazor端编写登录组件
以密码模式为例(需确保OpenIddict已启用密码授权类型):
@page "/login" @inject HttpClient Http @inject NavigationManager NavManager @inject AuthenticationStateProvider AuthState <h3>用户登录</h3> <EditForm Model="@loginModel" OnValidSubmit="@HandleLogin"> <DataAnnotationsValidator /> <ValidationSummary /> <div class="mb-3"> <label class="form-label">邮箱</label> <InputText @bind-Value="loginModel.Email" class="form-control" /> </div> <div class="mb-3"> <label class="form-label">密码</label> <InputPassword @bind-Value="loginModel.Password" class="form-control" /> </div> <button type="submit" class="btn btn-primary">登录</button> <p class="mt-2">还没有账户?<a href="/register">立即注册</a></p> </EditForm> @code { private LoginModel loginModel = new(); private async Task HandleLogin() { var tokenRequest = new FormUrlEncodedContent(new Dictionary<string, string> { ["grant_type"] = "password", ["username"] = loginModel.Email, ["password"] = loginModel.Password, ["client_id"] = "BlazorClient", // 你的OpenIddict客户端ID ["scope"] = "openid profile api" // 匹配OpenIddict配置的作用域 }); var response = await Http.PostAsync("connect/token", tokenRequest); if (response.IsSuccessStatusCode) { var token = await response.Content.ReadFromJsonAsync<TokenResponse>(); // 更新认证状态 await ((ApiAuthorizationProvider)AuthState).NotifyUserAuthenticated(token.AccessToken); NavManager.NavigateTo("/"); } else { var error = await response.Content.ReadAsStringAsync(); // 显示登录失败信息 } } private class LoginModel { [Required, EmailAddress] public string Email { get; set; } [Required] public string Password { get; set; } } private class TokenResponse { public string AccessToken { get; set; } public string TokenType { get; set; } public int ExpiresIn { get; set; } } }
3. Blazor端编写注册组件
调用Server端注册API完成用户创建:
@page "/register" @inject HttpClient Http @inject NavigationManager NavManager <h3>用户注册</h3> <EditForm Model="@registerModel" OnValidSubmit="@HandleRegister"> <DataAnnotationsValidator /> <ValidationSummary /> <div class="mb-3"> <label class="form-label">邮箱</label> <InputText @bind-Value="registerModel.Email" class="form-control" /> </div> <div class="mb-3"> <label class="form-label">密码</label> <InputPassword @bind-Value="registerModel.Password" class="form-control" /> </div> <div class="mb-3"> <label class="form-label">确认密码</label> <InputPassword @bind-Value="registerModel.ConfirmPassword" class="form-control" /> </div> <button type="submit" class="btn btn-primary">注册</button> <p class="mt-2">已有账户?<a href="/login">立即登录</a></p> </EditForm> @code { private RegisterModel registerModel = new(); private async Task HandleRegister() { var response = await Http.PostAsJsonAsync("api/account/register", registerModel); if (response.IsSuccessStatusCode) { NavManager.NavigateTo("/login"); } else { var errors = await response.Content.ReadFromJsonAsync<Dictionary<string, string[]>>(); // 显示注册失败信息 } } private class RegisterModel { [Required, EmailAddress] public string Email { get; set; } [Required, StringLength(100, MinimumLength = 6)] public string Password { get; set; } [Compare("Password")] public string ConfirmPassword { get; set; } } }
内容的提问来源于stack exchange,提问作者hasrthur
相关产品推荐
相关产品推荐

