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

MVC集成Azure Entra ID认证:Pass方法重复执行及身份问题

问题修复方案

问题根源

  1. Pass方法执行两次:Startup.cs中重复调用了services.AddControllersWithViews(),导致MVC授权相关服务被重复注册,请求处理时触发两次授权检查。
  2. 存在两个身份:在授权需求中直接调用AddIdentity追加自定义身份,而非替换或整合原有Azure AD身份,同时授权逻辑的执行时机不符合ASP.NET Core设计规范。

修复步骤

1. 移除重复的MVC服务注册

打开Startup.cs,删除重复的services.AddControllersWithViews()调用:
原代码:

services.AddControllersWithViews();

var mvcBuilder = services.AddControllersWithViews();

修改为:

var mvcBuilder = services.AddControllersWithViews();

2. 使用Claims Transformation处理用户身份

创建专门的声明转换类,负责从数据库获取用户信息并替换/扩展用户身份(这是ASP.NET Core中处理用户声明的标准方式,只会执行一次):

public class CustomClaimsTransformer : IClaimsTransformation
{
    private readonly DataContext _dataContext;

    public CustomClaimsTransformer(DataContext dataContext)
    {
        _dataContext = dataContext;
    }

    public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        // 避免重复转换,检查是否已有自定义声明
        if (principal.HasClaim(c => c.Type == ClaimTypes.Role && c.Issuer == "Custom"))
        {
            return principal;
        }

        var email = principal.Identity?.Name;
        if (string.IsNullOrEmpty(email))
        {
            return principal;
        }

        var user = await _dataContext.Users
            .FirstOrDefaultAsync(x => x.Email == email && x.Active);

        if (user == null)
        {
            return principal;
        }

        // 创建自定义身份,使用原认证类型确保为主身份
        var customIdentity = new ClaimsIdentity(
            new[]
            {
                new Claim(ClaimTypes.Email, user.Email, ClaimValueTypes.String, "Custom"),
                new Claim(ClaimTypes.Name, user.Name, ClaimValueTypes.String, "Custom"),
                new Claim(ClaimTypes.Role, user.Role, ClaimValueTypes.String, "Custom")
            },
            principal.Identity.AuthenticationType);

        // 替换为自定义身份作为主身份(如需保留Azure AD声明,可添加原身份到新Principal)
        return new ClaimsPrincipal(customIdentity);
    }
}

在Startup.cs的ConfigureServices中注册该类:

services.AddScoped<IClaimsTransformation, CustomClaimsTransformer>();

3. 简化授权策略

授权逻辑只需要验证用户是否合法,无需修改身份。可以直接通过声明检查实现,无需自定义PolicyRequirement和PolicyAuthorizationHandler:
修改Startup.cs中的授权策略配置:

services.AddAuthorization(options => {
    options.AddPolicy("AzureAccount", policyBuilder => {
        policyBuilder.RequireAuthenticatedUser();
        // 检查是否存在自定义颁发的角色声明
        policyBuilder.RequireClaim(ClaimTypes.Role, issuer: "Custom");
    });
});

如果仍需保留自定义授权Handler,调整PolicyAuthorizationHandler仅做授权检查:

public class PolicyAuthorizationHandler : AuthorizationHandler<PolicyRequirement>
{
    private readonly DataContext _dataContext;

    public PolicyAuthorizationHandler(DataContext dataContext)
    {
        _dataContext = dataContext;
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PolicyRequirement requirement)
    {
        var email = context.User.Identity?.Name;
        if (string.IsNullOrEmpty(email))
        {
            context.Fail();
            return;
        }

        var userExists = await _dataContext.Users
            .AnyAsync(x => x.Email == email && x.Active);

        if (userExists)
        {
            context.Succeed(requirement);
        }
        else
        {
            context.Fail();
        }
    }
}

同时移除PolicyRequirement中的Pass方法,仅保留空的需求类:

public class PolicyRequirement : IAuthorizationRequirement
{
}

效果验证

完成上述修改后:

  • 授权检查逻辑只会执行一次
  • _httpContextAccessor.HttpContext.User.Identity将是你自定义的身份
  • 用户身份不会出现重复的情况

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 06:34:53