Blazor WebAssembly.Net5 MsalAuthentication中基于角色的授权



我的Blazor Web Assembly应用程序已在Azure Active Directory中注册(仅限我的组织的单个租户(。

组声明已添加到令牌配置中,并具有将组作为角色声明发出的可选设置。

我可以运行应用程序并进行身份验证,但任何具有Authorize属性的页面都会显示";您无权访问此资源"在App.razor中为未授权配置的消息

我已经确认我收到了角色声明,但它是一个角色名称的json数组。

我尝试创建一个自定义AccountClaimsPrincipalFactory,在其中反序列化角色声明值,并为数组中的每个角色名称添加一个新声明,但仍然得到相同的结果。

public class CustomAccountClaimsPrincipalFactory : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
public CustomAccountClaimsPrincipalFactory(IAccessTokenProviderAccessor accessor)
: base(accessor) { }
public async override ValueTask<ClaimsPrincipal> CreateUserAsync(RemoteUserAccount account, RemoteAuthenticationUserOptions options)
{
var user = await base.CreateUserAsync(account, options);
if (!user.Identity.IsAuthenticated)
{
return user;
}
if (user.Identity is not ClaimsIdentity identity)
{
return user;
}
var roleClaims = identity.FindAll("roles");
if (roleClaims is null || !roleClaims.Any())
{
return user;
}
foreach (var roleClaim in roleClaims)
{
try
{
var roleNames = JsonConvert.DeserializeObject<string[]>(roleClaim.Value);
foreach (var roleName in roleNames)
{
identity.AddClaim(new Claim(ClaimTypes.Role, roleName));
}
}
catch
{
// continue
}
}
return user;
}
}

在我的程序.cs 中

_ = builder.Services.AddMsalAuthentication(options =>
{
Configuration.Bind("AzureAD", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("app-scope");
})
.AddAccountClaimsPrincipalFactory<CustomAccountClaimsPrincipalFactory>();

在我的页面上

@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles="GROUP1, GROUP2, GROUP3")]

我已经确认AccountClaimsPrincipalFactory已经将每个角色添加为一个新的声明,并且我能够查看用户的配置文件信息并从user.IsInRole("GROUP1");获得真实的响应,但是,在任何具有[Authorize]属性的页面上,我仍然看到相同的错误:

您无权访问此资源

我是不是遗漏了什么?有什么建议吗?

解决方案是修改令牌配置,使用Group ID而不是sAMAcountName添加组声明,然后在页面的[Authorize]属性和AuthorizeView组件中使用Group的Object ID值。

@using Microsoft.AspNetCore.Authorization
[Authorize(Roles="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy, zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz")]
<div>
<p>Only user's in the "X", "Y", or "Z" role are authorized to view this page</p>
<AuthorizeView  Roles="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz">
<p>This content is only rendered for user's in the "X" or "Z" role</p>
</AuthorizeView>
<AuthorizeView  Roles="yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy">
<p>This content is only rendered for user's in the "Y" role</p>
</AuthorizeView>
</div>

我想我遇到了一个非常密切相关的问题。我的身份验证提供者(在我的情况下,我使用MsalAuthentication(正在返回具有名为"的声明的角色;角色";而不是预期的";http://schemas.microsoft.com/ws/2008/06/identity/claims/role",因此,使用Authorize(Roles = "some-role")保护的任何内容都将返回未经授权的内容,即使用户具有该角色也是如此。

我的解决方案是实现我自己的AuthenticationStateProvider并覆盖GetAuthenticatedUser方法:

public class RoleTransformingAuthenticationStateProvider : RemoteAuthenticationService<RemoteAuthenticationState, RemoteUserAccount, MsalProviderOptions>
{
public RoleTransformingAuthenticationStateProvider(
IJSRuntime jsRuntime,
IOptionsSnapshot<RemoteAuthenticationOptions<MsalProviderOptions>> options,
NavigationManager navigation,
AccountClaimsPrincipalFactory<RemoteUserAccount> accountClaimsPrincipalFactory)
: base(jsRuntime, options, navigation, accountClaimsPrincipalFactory)
{
}
protected override async ValueTask<ClaimsPrincipal> GetAuthenticatedUser()
{
var user = await base.GetAuthenticatedUser();
var rolesClaim = user?.FindFirst("roles");
if (rolesClaim is not null)
{
var roleList = JsonSerializer.Deserialize<IEnumerable<string>>(rolesClaim.Value);
var roles = roleList!.Select(x => new Claim(ClaimTypes.Role, x));
user.AddIdentity(new ClaimsIdentity(roles));
}
return user;
}
}

然后,我将我的AuthStateProvider添加到程序中的服务中。cs:

builder.Services.AddScoped<
AuthenticationStateProvider,
RoleTransformingAuthenticationStateProvider>();

最新更新