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

Asp.Net Core结合AzureAD外部登录时Identity角色验证失效问题

问题:Azure AD外部登录结合ASP.NET Core Identity后角色授权失效

系统原本使用Azure AD作为外部登录提供商,Program.cs配置如下:

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
                .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));

appsettings.json已正确配置clientId、tenantId等参数,运行正常。

为限制仅特定用户访问,引入ASP.NET Core Identity,创建用户管理页面从AD搜索添加用户并分配Admin等角色。通过添加登录登出的Scaffolded Item解决了初始的Identity与外部登录不兼容问题。

编写CheckSignIn方法用于用户AD登录后验证系统内是否存在该用户:

[HttpGet]
public async Task<IActionResult> CheckSignIn()
{
    if (User.Identity.IsAuthenticated)
    {
        var user = await userManager.FindByEmailAsync(User.Identity.Name);

        if (user != null)
        {
            await signInManager.SignInAsync(user, false);
            return RedirectToAction("Dashboard", "Dashboard");
        }
        else
        {
            await signInManager.SignOutAsync();
            return RedirectToAction("NoAccess", "ManageAccess");
        }
    }
    return RedirectToAction("NoAccess", "ManageAccess");
}

当前问题:给用户分配Admin角色后,带有[Authorize(Roles="Admin")]的控制器会将用户重定向到访问拒绝页;调用SignInManager.IsSignedIn(user)返回false,系统无法识别用户登录状态及角色权限。


解决方案

1. 修正认证Scheme的优先级配置

同时使用Azure AD和Identity时,需明确设置默认认证、挑战和登录Scheme,确保Identity的Cookie认证为核心,Azure AD作为外部登录提供商。修改Program.cs:

// 先注册Identity服务
builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = false)
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

// 配置认证及Azure AD外部登录
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));

2. 完善CheckSignIn的登录逻辑

原方法仅创建Identity会话但未合并Azure AD Claims、加载用户角色,调整如下:

[HttpGet]
public async Task<IActionResult> CheckSignIn()
{
    if (User.Identity.IsAuthenticated)
    {
        // 获取外部登录信息
        var externalLoginInfo = await signInManager.GetExternalLoginInfoAsync();
        if (externalLoginInfo == null)
        {
            await signInManager.SignOutAsync();
            return RedirectToAction("NoAccess", "ManageAccess");
        }

        var user = await userManager.FindByEmailAsync(User.Identity.Name);
        if (user != null)
        {
            // 关联外部登录并创建Identity会话
            var signInResult = await signInManager.ExternalLoginSignInAsync(
                externalLoginInfo.LoginProvider, 
                externalLoginInfo.ProviderKey, 
                isPersistent: false, 
                bypassTwoFactor: true);

            if (signInResult.Succeeded)
            {
                // 加载用户角色与Claims并更新认证主体
                var roles = await userManager.GetRolesAsync(user);
                var userClaims = await userManager.GetClaimsAsync(user);
                var identity = new ClaimsIdentity(IdentityConstants.ApplicationScheme);
                
                identity.AddClaims(userClaims);
                foreach (var role in roles)
                {
                    identity.AddClaim(new Claim(ClaimTypes.Role, role));
                }
                // 追加Azure AD中未重复的Claims(可选)
                identity.AddClaims(User.Claims.Where(c => !userClaims.Any(uc => uc.Type == c.Type)));

                await HttpContext.SignInAsync(IdentityConstants.ApplicationScheme, new ClaimsPrincipal(identity));
                return RedirectToAction("Dashboard", "Dashboard");
            }
        }
        await signInManager.SignOutAsync();
        return RedirectToAction("NoAccess", "ManageAccess");
    }
    return RedirectToAction("NoAccess", "ManageAccess");
}

3. 确保角色Claims被正确注入

注册自定义Claims工厂,保证用户角色被添加到认证主体中:

// 自定义Claims工厂类
public class CustomUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
    public CustomUserClaimsPrincipalFactory(
        UserManager<ApplicationUser> userManager, 
        RoleManager<IdentityRole> roleManager, 
        IOptions<IdentityOptions> optionsAccessor) 
        : base(userManager, roleManager, optionsAccessor)
    {
    }

    protected override async Task<ClaimsIdentity> GenerateClaimsAsync(ApplicationUser user)
    {
        var identity = await base.GenerateClaimsAsync(user);
        var roles = await UserManager.GetRolesAsync(user);
        foreach (var role in roles)
        {
            identity.AddClaim(new Claim(ClaimTypes.Role, role));
        }
        return identity;
    }
}

// 在Program.cs中注册
builder.Services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, CustomUserClaimsPrincipalFactory>();

4. 验证Authorize属性的Scheme配置

确保控制器的授权属性使用Identity的Scheme,而非Azure AD的Scheme:

// 正确示例:使用Identity的Application Scheme
[Authorize(Roles = "Admin", AuthenticationSchemes = IdentityConstants.ApplicationScheme)]
// 或依赖默认配置(已设置DefaultAuthenticateScheme为Identity Scheme)
[Authorize(Roles = "Admin")]

5. 确认用户角色已正确保存

检查数据库AspNetUserRoles表,确认目标用户与Admin角色的关联记录存在;也可通过代码验证:

var user = await userManager.FindByEmailAsync("目标用户邮箱");
var assignedRoles = await userManager.GetRolesAsync(user);
// 输出assignedRoles确认包含Admin

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 23:35:23