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

ASP.NET Core Identity如何强制未确认邮箱的已认证用户跳转至确认页?

处理ASP.NET Core Identity中已认证但未确认邮箱用户的跳转问题

你提到的场景确实是Identity的常见需求,官方没有直接提供EmailConfirmationPath这类配置,但可以通过以下几种标准方式实现:

一、自定义授权策略(推荐,贴合Identity授权体系)

这种方式通过授权策略拦截未确认邮箱的已认证用户,比中间件更符合ASP.NET Core的授权设计:

  1. 创建自定义授权要求与处理程序
// 自定义授权要求
public class EmailConfirmedRequirement : IAuthorizationRequirement { }

// 授权处理程序
public class EmailConfirmedAuthorizationHandler : AuthorizationHandler<EmailConfirmedRequirement>
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public EmailConfirmedAuthorizationHandler(UserManager<IdentityUser> userManager, IHttpContextAccessor httpContextAccessor)
    {
        _userManager = userManager;
        _httpContextAccessor = httpContextAccessor;
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, EmailConfirmedRequirement requirement)
    {
        var user = await _userManager.GetUserAsync(_httpContextAccessor.HttpContext.User);
        if (user == null)
        {
            context.Fail();
            return;
        }

        // 排除邮箱确认相关页面,避免死循环
        var path = _httpContextAccessor.HttpContext.Request.Path;
        if (path.StartsWithSegments("/Account/ConfirmEmail") || path.StartsWithSegments("/Account/ResendConfirmationEmail"))
        {
            context.Succeed(requirement);
            return;
        }

        if (await _userManager.IsEmailConfirmedAsync(user))
        {
            context.Succeed(requirement);
        }
        else
        {
            // 跳转至邮箱确认页
            _httpContextAccessor.HttpContext.Response.Redirect("/Account/ConfirmEmail");
            context.Succeed(requirement); // 标记为成功避免后续授权拦截
        }
    }
}
  1. 注册授权策略
    在Program.cs中添加:
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("RequireEmailConfirmed", policy =>
        policy.Requirements.Add(new EmailConfirmedRequirement()));
});

// 注册授权处理程序
builder.Services.AddScoped<IAuthorizationHandler, EmailConfirmedAuthorizationHandler>();
  1. 应用策略
  • 全局应用(所有路由生效,除了排除的页面):
app.UseAuthorization();
app.MapControllers().RequireAuthorization("RequireEmailConfirmed");
  • 或针对特定控制器/Action添加[Authorize(Policy = "RequireEmailConfirmed")]

二、自定义中间件(灵活直接)

这是你最初的思路,优化后避免死循环即可:

public class EmailConfirmationMiddleware
{
    private readonly RequestDelegate _next;

    public EmailConfirmationMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context, UserManager<IdentityUser> userManager, SignInManager<IdentityUser> signInManager)
    {
        if (signInManager.IsSignedIn(context.User))
        {
            var user = await userManager.GetUserAsync(context.User);
            if (user != null && !await userManager.IsEmailConfirmedAsync(user))
            {
                // 排除邮箱确认相关路径
                var path = context.Request.Path.Value;
                if (!path.Contains("/Account/ConfirmEmail") && !path.Contains("/Account/ResendConfirmationEmail"))
                {
                    context.Response.Redirect("/Account/ConfirmEmail");
                    return;
                }
            }
        }

        await _next(context);
    }
}

// 注册中间件(放在UseAuthentication和UseAuthorization之间)
app.UseAuthentication();
app.UseMiddleware<EmailConfirmationMiddleware>();
app.UseAuthorization();

三、登录后即时跳转(补充方案)

在登录逻辑中,验证用户登录成功后直接检查邮箱状态,跳转至确认页:

public async Task<IActionResult> Login(LoginViewModel model)
{
    if (ModelState.IsValid)
    {
        var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
        if (result.Succeeded)
        {
            var user = await _userManager.FindByEmailAsync(model.Email);
            if (!await _userManager.IsEmailConfirmedAsync(user))
            {
                return RedirectToAction("ConfirmEmail", "Account");
            }
            return RedirectToLocal(model.ReturnUrl);
        }
        // 其他登录失败逻辑
    }
    return View(model);
}

注意事项

  • 务必确保邮箱确认页面和重发邮件的接口不被拦截,否则会出现无限跳转的死循环。
  • 重发邮件的接口需要允许未确认邮箱的用户访问,让用户可以重新获取确认令牌。

内容的提问来源于stack exchange,提问作者IvanD

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 11:52:40