如何通过Azure认证为ASP.NET MVC与Web API添加自定义声明并实现角色授权
Azure Authentication下ASP.NET MVC与Web API的自定义声明及角色认证实现
一、核心需求拆解
你需要实现两个核心目标:
- 让MVC应用获取的访问令牌包含自定义角色(如
SomeRole),供API端做授权校验 - 为MVC应用本身配置基于角色的身份认证逻辑
二、为访问令牌添加自定义角色声明
方式1:Azure AD静态配置应用角色(推荐,适合固定角色)
这是最标准的实现方式,角色声明会直接包含在Azure AD签发的访问令牌中:
- 登录Azure门户,找到你的API应用注册(对应
api://123) - 进入「应用角色」页面,点击「创建应用角色」,添加角色(名称填
SomeRole,允许的成员类型选「用户/组」) - 切换到你的MVC应用注册,进入「API权限」页面,点击「添加权限」→ 选择你的API → 勾选刚创建的
SomeRole权限,最后点击「授予管理员同意」 - 完成后,用户登录MVC应用时,获取的访问令牌会自动包含
roles声明,值为SomeRole
方式2:动态添加本地声明(适合业务逻辑驱动的动态角色)
如果角色需要根据业务逻辑动态生成,无法在Azure AD静态配置,可通过以下方式处理:
- MVC端在用户登录时,通过
OnTokenValidated事件将角色添加到本地认证主体(仅用于MVC自身的授权) - 调用API时,将自定义声明通过请求头传递给API(因为Azure AD签发的访问令牌无法客户端修改)
三、ASP.NET MVC端配置基于角色的身份认证
1. 修改Program.cs配置
更新认证服务,添加角色声明并启用授权:
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApp(options => { builder.Configuration.Bind("AzureAd", options); options.ClientSecret = "89898"; options.SaveTokens = true; options.Events ??= new OpenIdConnectEvents(); // 登录后为用户添加自定义声明与角色 options.Events.OnTokenValidated += async context => { // 可替换为从数据库/业务服务获取用户角色的逻辑 // var roleService = context.HttpContext.RequestServices.GetRequiredService<IRoleService>(); // var userRoles = await roleService.GetRolesForUser(context.Principal.FindFirstValue(ClaimTypes.ObjectIdentifier)); var customClaims = new List<Claim> { new Claim(ClaimTypes.Role, "SomeRole"), // 添加角色声明 new Claim("Foo", "Bar") // 添加自定义声明 }; context.Principal.AddIdentity(new ClaimsIdentity(customClaims)); // 更新认证票证,确保声明生效 await context.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, context.Principal); }; }) .EnableTokenAcquisitionToCallDownstreamApi(new string[] { "api://123/.default" }) .AddInMemoryTokenCaches(); // 添加授权服务,可自定义角色策略 builder.Services.AddAuthorization(options => { options.AddPolicy("RequireSomeRole", policy => policy.RequireRole("SomeRole")); }); // 启用中间件(顺序不能错) app.UseAuthentication(); app.UseAuthorization();
2. 在MVC控制器中使用角色授权
在需要保护的Action或Controller上添加[Authorize]特性:
// 直接指定角色 [Authorize(Roles = "SomeRole")] public IActionResult AdminDashboard() { return View(); } // 或者使用自定义策略 [Authorize(Policy = "RequireSomeRole")] public IActionResult ProtectedAction() { return View(); }
四、ASP.NET Web API端处理自定义声明与授权
情况1:使用Azure AD配置的应用角色
- 修改Program.cs,启用授权服务:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApi(options => { builder.Configuration.Bind("AzureAd", options); options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = $"https://sts.windows.net/{builder.Configuration["AzureAd:TenantId"]}/", ValidateAudience = true, ValidAudience = "api://123", ValidateLifetime = true, ClockSkew = TimeSpan.FromMinutes(5) }; }); // 添加授权策略 builder.Services.AddAuthorization(options => { options.AddPolicy("RequireSomeRole", policy => policy.RequireRole("SomeRole")); }); // 启用中间件 app.UseAuthentication(); app.UseAuthorization();
- 在API控制器中使用授权并获取声明:
[Authorize(Roles = "SomeRole")] [ApiController] [Route("api/[controller]")] public class DataController : ControllerBase { [HttpGet] public IActionResult GetData() { // 从令牌中获取角色与自定义声明 var someRole = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Role && c.Value == "SomeRole")?.Value; var fooClaim = User.Claims.FirstOrDefault(c => c.Type == "Foo")?.Value; return Ok(new { Message = "授权成功", Role = someRole, CustomClaim = fooClaim }); } }
情况2:使用动态传递的自定义声明
如果自定义声明是MVC端动态生成的,未包含在访问令牌中,需通过请求头传递:
- MVC端调用API时添加请求头:
private readonly ITokenAcquisition _tokenAcquisition; private readonly IHttpClientFactory _httpClientFactory; public YourMvcController(ITokenAcquisition tokenAcquisition, IHttpClientFactory httpClientFactory) { _tokenAcquisition = tokenAcquisition; _httpClientFactory = httpClientFactory; } public async Task<IActionResult> CallApi() { var accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(new[] { "api://123/.default" }); var client = _httpClientFactory.CreateClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); // 传递自定义声明到API var fooClaim = User.Claims.FirstOrDefault(c => c.Type == "Foo"); if (fooClaim != null) { client.DefaultRequestHeaders.Add("X-Foo-Claim", fooClaim.Value); } var response = await client.GetAsync("https://your-api-url/api/data"); var data = await response.Content.ReadFromJsonAsync<YourDataModel>(); return View(data); }
- API端从请求头获取声明:
[HttpGet] public IActionResult GetData() { var fooValue = Request.Headers["X-Foo-Claim"].FirstOrDefault(); return Ok(new { Message = "授权成功", CustomClaimValue = fooValue }); }
五、关键注意事项
- Azure AD签发的访问令牌不可客户端修改,所有需在令牌中体现的声明必须通过Azure AD配置(应用角色、用户扩展属性等)
- MVC端
OnTokenValidated事件添加的声明仅存在于本地Cookie认证票证,不会自动同步到访问令牌 - API端默认会自动解析Azure AD的角色声明,无需额外配置
RoleClaimType(Microsoft Identity Web已默认适配)
内容的提问来源于stack exchange,提问作者ZCoder
相关产品推荐
相关产品推荐

