You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor Server使用AspNetCore.Identity如何强制邮箱验证生效

问题根因

你配置的options.SignIn.RequireConfirmedAccount = true默认仅作用于Identity自带的密码登录校验逻辑,不会主动集成到角色授权、自定义登录/IdentityServer认证流程中。因此即使用户邮箱未确认,只要身份认证通过、角色Claim正常加载,就可以通过你当前写的角色授权校验。

你不需要在用户确认邮箱前删除其持久化的角色关联数据,直接在认证或授权层加校验规则即可,避免后续邮箱确认后还要重新赋权的冗余操作,推荐以下两种落地方式:


方案1:自定义授权策略(适用需要区分部分页面要邮箱确认、部分不需要的场景)

无需修改现有角色关联逻辑,仅对受限资源新增带邮箱确认要求的授权规则即可:

  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;
    }
}
  1. 修改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");
    });
});
  1. 替换你原来的授权校验写法即可:
  • 页面级校验:@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 13:36:02