本文提供有关解决开发 WebAssembly 身份验证应用中基于角色的访问控制问题的指南。
症状
生成 WebAssembly 身份验证应用并尝试在应用中实现基于角色的访问控制时,会收到以下错误消息:
- 无权访问此资源。
- Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]授权失败。 未满足这些要求:RolesAuthorizationRequirement:User.IsInRole 必须满足以下角色之一:(ROLE_NAME)
原因
WebAssembly 身份验证堆栈可能会将角色声明强制转换为单个字符串。 这可以防止适当的基于角色的访问控制。
解决方案
你可以实现自定义用户工厂来修改角色声明映射的行为。 为此,请按照下列步骤操作。
步骤 1:创建自定义用户工厂
创建自定义用户工厂(CustomUserFactory.cs):
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;
using System.Security.Claims;
using System.Text.Json;
public class CustomUserFactory : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
public CustomUserFactory(IAccessTokenProviderAccessor accessor)
: base(accessor)
{
}
public async override ValueTask<ClaimsPrincipal> CreateUserAsync(
RemoteUserAccount account,
RemoteAuthenticationUserOptions options)
{
var user = await base.CreateUserAsync(account, options);
var claimsIdentity = (ClaimsIdentity?)user.Identity;
if (account != null && claimsIdentity != null)
{
MapArrayClaimsToMultipleSeparateClaims(account, claimsIdentity);
}
return user;
}
private void MapArrayClaimsToMultipleSeparateClaims(RemoteUserAccount account, ClaimsIdentity claimsIdentity)
{
foreach (var prop in account.AdditionalProperties)
{
var key = prop.Key;
var value = prop.Value;
if (value != null && (value is JsonElement element && element.ValueKind == JsonValueKind.Array))
{
// Remove the Roles claim with an array value, and create new claims with the same key
claimsIdentity.RemoveClaim(claimsIdentity.FindFirst(prop.Key));
var claims = element.EnumerateArray().Select(x => new Claim(prop.Key, x.ToString()));
claimsIdentity.AddClaims(claims);
}
}
}
}
步骤 2:将角色映射和自定义用户工厂添加到身份验证中间件
如果使用的是 AddOidcAuthentication
:
builder.Services.AddOidcAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions);
options.ProviderOptions.AdditionalProviderParameters.Add("domain_hint", "contoso.com");
options.ProviderOptions.DefaultScopes.Add("User.Read");
options.UserOptions.RoleClaim = "roles";
options.ProviderOptions.ResponseType = "code";
}).AddAccountClaimsPrincipalFactory<CustomUserFactory>();
如果使用的是 AddMsalAuthentication
:
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.AdditionalScopesToConsent.Add("user.read");
options.ProviderOptions.DefaultAccessTokenScopes.Add("api://{your-api-id}");
options.UserOptions.RoleClaim = "roles";
}).AddAccountClaimsPrincipalFactory<CustomUserFactory>();
步骤 3:将 Authorize
属性添加到 Blazor 页面
@attribute [Authorize(Roles="access_as_user")]
接下来,将应用角色添加到应用注册,将用户分配到应用角色,然后将应用配置为使用分配的应用角色。