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

.NET 6中使用Azure AD认证,数据库管理角色的实现方案

解决方案

核心思路是在Azure AD令牌验证完成后,从自定义数据库加载用户角色并更新用户的Claims集合,让默认的[Authorize(Roles)]属性直接识别这些数据库角色,无需自定义授权属性。

步骤1:修改认证配置,注入数据库角色

在AddMicrosoftIdentityWebApi的配置中,通过OnTokenValidated事件替换用户的角色声明:

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(options =>
    {
        builder.Configuration.Bind("AzureAd", options);
        
        // 禁用Azure AD自动注入角色声明,避免和数据库角色冲突
        options.TokenValidationParameters.RoleClaimType = null;
        
        options.Events = new JwtBearerEvents
        {
            OnTokenValidated = async context =>
            {
                // 获取Azure用户的唯一标识(OID)
                var azureUserId = context.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
                
                // 从DI容器获取自定义角色服务
                var roleService = context.HttpContext.RequestServices.GetRequiredService<IRoleService>();
                var userDbRoles = await roleService.GetUserRolesAsync(azureUserId);
                
                // 清理原有角色声明,替换为数据库角色
                var identity = context.Principal.Identity as ClaimsIdentity;
                identity?.RemoveAll(ClaimTypes.Role);
                
                foreach (var role in userDbRoles)
                {
                    identity?.AddClaim(new Claim(ClaimTypes.Role, role));
                }
            }
        };
    }, builder.Configuration, "AzureAd");

步骤2:实现数据库角色查询服务

创建一个服务类,负责根据Azure用户ID从自定义的Roles和UserRoles表中查询角色:

定义服务接口

public interface IRoleService
{
    Task<List<string>> GetUserRolesAsync(string azureUserId);
}

实现服务逻辑

public class RoleService : IRoleService
{
    private readonly AppDbContext _dbContext;

    public RoleService(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task<List<string>> GetUserRolesAsync(string azureUserId)
    {
        return await _dbContext.UserRoles
            .Where(ur => ur.AzureUserId == azureUserId)
            .Join(
                _dbContext.Roles,
                ur => ur.RoleId,
                r => r.Id,
                (ur, r) => r.Name
            )
            .ToListAsync();
    }
}

注册服务到DI容器

builder.Services.AddScoped<IRoleService, RoleService>();

步骤3:使用默认[Authorize(Roles)]属性

现在可以直接用默认的授权属性,它会自动检查我们注入的数据库角色:

[Authorize(Roles = "App1.Admin, App1.User")]
[HttpGet("app1/protected-data")]
public IActionResult GetApp1ProtectedData()
{
    return Ok("仅App1的管理员或用户可访问此内容");
}

额外优化建议

  • 缓存角色:为避免每次请求都查询数据库,可添加缓存逻辑(比如IMemoryCache),用Azure用户ID作为缓存键,角色变更时清空对应缓存。
  • 权限粒度控制:如果需要更细粒度的权限(比如资源级授权),可以结合IAuthorizationHandler实现自定义权限策略,但角色级授权用上述方案足够。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 02:18:31