Blazor Server使用AspNetCore.Identity如何强制邮箱验证生效
问题根因
你配置的options.SignIn.RequireConfirmedAccount = true默认仅作用于Identity自带的密码登录校验逻辑,不会主动集成到角色授权、自定义登录/IdentityServer认证流程中。因此即使用户邮箱未确认,只要身份认证通过、角色Claim正常加载,就可以通过你当前写的角色授权校验。
你不需要在用户确认邮箱前删除其持久化的角色关联数据,直接在认证或授权层加校验规则即可,避免后续邮箱确认后还要重新赋权的冗余操作,推荐以下两种落地方式:
方案1:自定义授权策略(适用需要区分部分页面要邮箱确认、部分不需要的场景)
无需修改现有角色关联逻辑,仅对受限资源新增带邮箱确认要求的授权规则即可:
- 首先替换你现有的Claims工厂,把邮箱确认状态加入用户声明中:
// 自定义Claims工厂类 public class CustomClaimsFactory : UserClaimsPrincipalFactory<CustomUserContext, IdentityRole> { public CustomClaimsFactory(UserManager<CustomUserContext> userManager, RoleManager<IdentityRole> roleManager, IOptions<IdentityOptions> optionsAccessor) : base(userManager, roleManager, optionsAccessor) { } protected override async Task<ClaimsIdentity> GenerateClaimsAsync(CustomUserContext user) { var identity = await base.GenerateClaimsAsync(user); // 新增邮箱确认状态的声明 identity.AddClaim(new Claim("EmailConfirmed", user.EmailConfirmed.ToString().ToLower())); return identity; } }
- 修改
Startup.cs的服务注册,替换Claims工厂、新增授权策略:
services.AddDefaultIdentity<CustomUserContext>(options => options.SignIn.RequireConfirmedAccount = true) .AddRoles<IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() // 替换为自定义的Claims工厂 .AddClaimsPrincipalFactory<CustomClaimsFactory>(); // 新增授权策略配置 services.AddAuthorization(options => { options.AddPolicy("ConfirmedRoleAccess", policy => { policy.RequireAuthenticatedUser(); policy.RequireClaim("EmailConfirmed", "true"); // 可按需指定要求的角色,也可以动态传参 policy.RequireRole("some_user_role"); }); });
- 替换你原来的授权校验写法即可:
- 页面级校验:
@attribute [Authorize(Policy = "ConfirmedRoleAccess")] - 局部视图校验:
<AuthorizeView Policy="ConfirmedRoleAccess"></AuthorizeView>
方案2:未确认邮箱时不返回角色Claim(适用所有角色权限都要求邮箱确认的场景)
不需要修改你现有任何授权代码,仅改造Claims工厂,邮箱未确认的用户自动不带角色声明,原有角色校验逻辑自动生效:
// 自定义Claims工厂类 public class CustomClaimsFactory : UserClaimsPrincipalFactory<CustomUserContext, IdentityRole> { public CustomClaimsFactory(UserManager<CustomUserContext> userManager, RoleManager<IdentityRole> roleManager, IOptions<IdentityOptions> optionsAccessor) : base(userManager, roleManager, optionsAccessor) { } protected override async Task<ClaimsIdentity> GenerateClaimsAsync(CustomUserContext user) { var identity = await base.GenerateClaimsAsync(user); // 邮箱未确认时移除所有角色声明 if (!user.EmailConfirmed) { var roleClaims = identity.FindAll(identity.RoleClaimType).ToList(); foreach (var claim in roleClaims) { identity.RemoveClaim(claim); } } return identity; } }
修改后同样替换Startup.cs里的Claims工厂注册即可,原有[Authorize(Roles = "xxx")]、<AuthorizeView Roles="xxx">的写法完全不需要改动。
内容的提问来源于stack exchange,提问作者jason
相关产品推荐
相关产品推荐

