Blazor Server中ASP.NET Identity实时权限更新与禁用用户校验实现
Blazor Server + ASP.NET Identity 需求实现方案
1. 修改Claims无需登出立即生效
Blazor Server默认的ServerAuthenticationStateProvider会缓存初始登录后的身份信息,要实现Claims实时更新,需自定义身份状态提供者,每次获取身份时从数据库拉取最新Claims:
- 第一步:创建自定义身份状态提供者
public class CustomAuthStateProvider : ServerAuthenticationStateProvider { private readonly UserManager<ApplicationUser> _userManager; private readonly IHttpContextAccessor _httpContextAccessor; public CustomAuthStateProvider(UserManager<ApplicationUser> userManager, IHttpContextAccessor httpContextAccessor) { _userManager = userManager; _httpContextAccessor = httpContextAccessor; } public override async Task<AuthenticationState> GetAuthenticationStateAsync() { var authState = await base.GetAuthenticationStateAsync(); var user = authState.User; if (user.Identity?.IsAuthenticated == true) { var appUser = await _userManager.FindByNameAsync(user.Identity.Name); if (appUser != null) { // 重新生成包含最新Claims的身份对象 var claims = await _userManager.GetClaimsAsync(appUser); var newIdentity = new ClaimsIdentity(claims, user.Identity.AuthenticationType); var newUser = new ClaimsPrincipal(newIdentity); // 更新缓存的身份状态 SetAuthenticationState(Task.FromResult(new AuthenticationState(newUser))); return new AuthenticationState(newUser); } } return authState; } // 提供主动刷新方法,用于Claims修改后触发前端更新 public async Task RefreshAuthenticationState() { NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); } }
- 第二步:注册自定义Provider
在Program.cs中替换默认服务:
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
- 第三步:修改Claims后主动触发刷新
在后台修改用户Claims的代码后,调用刷新方法通知前端:
// 注入CustomAuthStateProvider后调用 await _customAuthStateProvider.RefreshAuthenticationState();
2. 页面跳转时重新读取Claims(保留登录缓存)
通过监听页面导航事件,每次跳转完成后触发身份状态刷新,既保留登录Cookie缓存,又能重新读取最新Claims:
在MainLayout.razor中注入服务并监听导航事件:
@inject NavigationManager NavigationManager @inject CustomAuthStateProvider AuthStateProvider @implements IDisposable @code { protected override void OnInitialized() { NavigationManager.LocationChanged += OnLocationChanged; } private async void OnLocationChanged(object sender, LocationChangedEventArgs e) { // 页面跳转完成后刷新身份状态 await AuthStateProvider.RefreshAuthenticationState(); } public void Dispose() { NavigationManager.LocationChanged -= OnLocationChanged; } }
3. 添加Enable列并拦截禁用用户
步骤1:扩展IdentityUser模型
修改自定义用户模型,添加Enable属性:
public class ApplicationUser : IdentityUser { public bool Enable { get; set; } = true; // 默认启用用户 }
步骤2:更新数据库表
创建并执行EF Core迁移,将Enable列添加到AspNetUsers表:
dotnet ef migrations add AddEnableColumnToUsers dotnet ef database update
步骤3:身份验证时校验启用状态
修改CustomAuthStateProvider的GetAuthenticationStateAsync方法,加入禁用用户校验:
public override async Task<AuthenticationState> GetAuthenticationStateAsync() { var authState = await base.GetAuthenticationStateAsync(); var user = authState.User; if (user.Identity?.IsAuthenticated == true) { var appUser = await _userManager.FindByNameAsync(user.Identity.Name); if (appUser != null) { // 用户被禁用时返回匿名身份,触发[Authorize]拦截 if (!appUser.Enable) { var anonymousUser = new ClaimsPrincipal(new ClaimsIdentity()); SetAuthenticationState(Task.FromResult(new AuthenticationState(anonymousUser))); return new AuthenticationState(anonymousUser); } // 正常更新Claims逻辑 var claims = await _userManager.GetClaimsAsync(appUser); var newIdentity = new ClaimsIdentity(claims, user.Identity.AuthenticationType); var newUser = new ClaimsPrincipal(newIdentity); SetAuthenticationState(Task.FromResult(new AuthenticationState(newUser))); return new AuthenticationState(newUser); } } return authState; }
可选:自定义授权特性强化校验
如果需要覆盖更多场景(如API接口),可自定义授权特性:
public class EnableUserAuthorizeAttribute : AuthorizeAttribute { protected override Task HandleUnauthorizedAsync(AuthorizationHandlerContext context) { var userManager = context.HttpContext.RequestServices.GetRequiredService<UserManager<ApplicationUser>>(); var user = userManager.GetUserAsync(context.User).Result; if (user != null && !user.Enable) { context.Fail(new AuthorizationFailureReason(this, "用户已被禁用")); } return base.HandleUnauthorizedAsync(context); } }
使用时将[Authorize]替换为[EnableUserAuthorize]即可。
内容的提问来源于stack exchange,提问作者David Thielen
相关产品推荐
相关产品推荐

