不强制执行 webassembly blazor 客户端应用程序中的授权视图
Authorization views in webassembly blazor client app are not enforced
我有一个在 webassembly blazor 中开发并受 Azure AD 保护的客户端应用程序,其中已注册并定义了 3 个角色并将其分配给用户。
用户登录后,用户将被重定向到显示声明集的个人资料页面
Claim Type Value
oid 091fadf9-b0bd-4583-b55d-XXXX
preferred_username XX
roles ["Administrator"]
根据用户角色不同 UI 将显示,但是这不起作用
<AuthorizeView Roles="Administrator">
ADMIN UI
</AuthorizeView>
我的程序 class 看起来像这样:
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
//builder.Logging.SetMinimumLevel(LogLevel.Debug);
////builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped<CustomAuthorizationMessageHandler>();
builder.Services.AddHttpClient("myAPI",
client => client.BaseAddress = new Uri("private API URL"))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("myAPI"));
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add(myAPI);
options.UserOptions.RoleClaim = "roles";
});
await builder.Build().RunAsync();
}
我支持 class 用户:
public class UserClaimsBase: ComponentBase
{
// AuthenticationStateProvider service provides the current user's ClaimsPrincipal data.
[Inject]
private AuthenticationStateProvider AuthenticationStateProvider { get; set;
}
protected string _authMessage;
protected IEnumerable<Claim> _claims = Enumerable.Empty<Claim>();
// Defines list of claim types that will be displayed after successfull sign-in.
private string[] returnClaims = { "name", "preferred_username", "tid", "oid", "roles" };
protected override async Task OnInitializedAsync()
{
await GetClaimsPrincipalData();
}
/// <summary>
/// Retrieves user claims for the signed-in user.
/// </summary>
/// <returns></returns>
private async Task GetClaimsPrincipalData()
{
// Gets an AuthenticationState that describes the current user.
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
// Checks if the user has been authenticated.
if (user.Identity.IsAuthenticated)
{
_authMessage = $"{user.Identity.Name} is authenticated.";
// Sets the claims value in _claims variable.
// The claims mentioned in returnClaims variable are selected only.
_claims = user.Claims.Where(x => returnClaims.Contains(x.Type));
}
else
{
_authMessage = "The user is NOT authenticated.";
}
}
}
这里有什么问题吗?
我不确定我缺少什么?
感谢任何帮助。
问题请参考以下步骤
- 在您的 Azure AD 应用程序中定义 appRole
在应用清单中添加以下内容
"appRoles": [
{
"allowedMemberTypes": [
"User",
"Application"
],
"description": "admin",
"displayName": "Admin",
"id": "d1c2ade8-98f8-45fd-aa4a-6d06b957c66f",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "admin"
},
{
"allowedMemberTypes": [
"User",
"Application"
],
"description": "access database test",
"displayName": "Test",
"id": "d1c2ade8-98f8-45fd-aa4a-6d06b947c66f",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "test"
}
],
-
配置应用程序
一个。包裹
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="3.2.1" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Build" Version="3.2.1" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="3.2.1" PrivateAssets="all" />
<PackageReference Include="Microsoft.Authentication.WebAssembly.Msal" Version="3.2.1" />
<PackageReference Include="System.Net.Http.Json" Version="3.2.0" />
b。扩展 RemoteUserAccount 以包含角色的属性
using System;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
public class CustomUserAccount : RemoteUserAccount
{
[JsonPropertyName("roles")]
public string[] Roles { get; set; } = Array.Empty<string>();
}
b。定义自定义用户工厂以转换 appRole 声明
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Graph;
public class CustomAccountFactory
: AccountClaimsPrincipalFactory<CustomUserAccount>
{
private readonly ILogger<CustomAccountFactory> logger;
private readonly IServiceProvider serviceProvider;
public CustomAccountFactory(IAccessTokenProviderAccessor accessor,
IServiceProvider serviceProvider,
ILogger<CustomAccountFactory> logger)
: base(accessor)
{
this.serviceProvider = serviceProvider;
this.logger = logger;
}
public async override ValueTask<ClaimsPrincipal> CreateUserAsync(
CustomUserAccount account,
RemoteAuthenticationUserOptions options)
{
var initialUser = await base.CreateUserAsync(account, options);
if (initialUser.Identity.IsAuthenticated)
{
var userIdentity = (ClaimsIdentity)initialUser.Identity;
foreach (var role in account.Roles)
{
userIdentity.AddClaim(new Claim("appRole", role));
}
}
return initialUser;
}
}
c。在 Program.cs
中配置 Azure AD 身份验证
builder.Services.AddMsalAuthentication<RemoteAuthenticationState,
CustomUserAccount>(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.UserOptions.RoleClaim = "appRole";
}).AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, CustomUserAccount,
CustomAccountFactory>();
- 授权
# add the following code in the page according to your need
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "admin")]
- 测试
我的页面
@page "/counter"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "admin")]
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private async Task IncrementCount()
{
currentCount++;
}
}
我的用户
结果
我有一个在 webassembly blazor 中开发并受 Azure AD 保护的客户端应用程序,其中已注册并定义了 3 个角色并将其分配给用户。
用户登录后,用户将被重定向到显示声明集的个人资料页面
Claim Type Value
oid 091fadf9-b0bd-4583-b55d-XXXX
preferred_username XX
roles ["Administrator"]
根据用户角色不同 UI 将显示,但是这不起作用
<AuthorizeView Roles="Administrator">
ADMIN UI
</AuthorizeView>
我的程序 class 看起来像这样:
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
//builder.Logging.SetMinimumLevel(LogLevel.Debug);
////builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped<CustomAuthorizationMessageHandler>();
builder.Services.AddHttpClient("myAPI",
client => client.BaseAddress = new Uri("private API URL"))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("myAPI"));
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add(myAPI);
options.UserOptions.RoleClaim = "roles";
});
await builder.Build().RunAsync();
}
我支持 class 用户:
public class UserClaimsBase: ComponentBase
{
// AuthenticationStateProvider service provides the current user's ClaimsPrincipal data.
[Inject]
private AuthenticationStateProvider AuthenticationStateProvider { get; set;
}
protected string _authMessage;
protected IEnumerable<Claim> _claims = Enumerable.Empty<Claim>();
// Defines list of claim types that will be displayed after successfull sign-in.
private string[] returnClaims = { "name", "preferred_username", "tid", "oid", "roles" };
protected override async Task OnInitializedAsync()
{
await GetClaimsPrincipalData();
}
/// <summary>
/// Retrieves user claims for the signed-in user.
/// </summary>
/// <returns></returns>
private async Task GetClaimsPrincipalData()
{
// Gets an AuthenticationState that describes the current user.
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
// Checks if the user has been authenticated.
if (user.Identity.IsAuthenticated)
{
_authMessage = $"{user.Identity.Name} is authenticated.";
// Sets the claims value in _claims variable.
// The claims mentioned in returnClaims variable are selected only.
_claims = user.Claims.Where(x => returnClaims.Contains(x.Type));
}
else
{
_authMessage = "The user is NOT authenticated.";
}
}
}
这里有什么问题吗? 我不确定我缺少什么? 感谢任何帮助。
问题请参考以下步骤
- 在您的 Azure AD 应用程序中定义 appRole
在应用清单中添加以下内容
"appRoles": [
{
"allowedMemberTypes": [
"User",
"Application"
],
"description": "admin",
"displayName": "Admin",
"id": "d1c2ade8-98f8-45fd-aa4a-6d06b957c66f",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "admin"
},
{
"allowedMemberTypes": [
"User",
"Application"
],
"description": "access database test",
"displayName": "Test",
"id": "d1c2ade8-98f8-45fd-aa4a-6d06b947c66f",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "test"
}
],
配置应用程序
一个。包裹
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="3.2.1" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Build" Version="3.2.1" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="3.2.1" PrivateAssets="all" />
<PackageReference Include="Microsoft.Authentication.WebAssembly.Msal" Version="3.2.1" />
<PackageReference Include="System.Net.Http.Json" Version="3.2.0" />
b。扩展 RemoteUserAccount 以包含角色的属性
using System;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
public class CustomUserAccount : RemoteUserAccount
{
[JsonPropertyName("roles")]
public string[] Roles { get; set; } = Array.Empty<string>();
}
b。定义自定义用户工厂以转换 appRole 声明
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Graph;
public class CustomAccountFactory
: AccountClaimsPrincipalFactory<CustomUserAccount>
{
private readonly ILogger<CustomAccountFactory> logger;
private readonly IServiceProvider serviceProvider;
public CustomAccountFactory(IAccessTokenProviderAccessor accessor,
IServiceProvider serviceProvider,
ILogger<CustomAccountFactory> logger)
: base(accessor)
{
this.serviceProvider = serviceProvider;
this.logger = logger;
}
public async override ValueTask<ClaimsPrincipal> CreateUserAsync(
CustomUserAccount account,
RemoteAuthenticationUserOptions options)
{
var initialUser = await base.CreateUserAsync(account, options);
if (initialUser.Identity.IsAuthenticated)
{
var userIdentity = (ClaimsIdentity)initialUser.Identity;
foreach (var role in account.Roles)
{
userIdentity.AddClaim(new Claim("appRole", role));
}
}
return initialUser;
}
}
c。在 Program.cs
builder.Services.AddMsalAuthentication<RemoteAuthenticationState,
CustomUserAccount>(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.UserOptions.RoleClaim = "appRole";
}).AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, CustomUserAccount,
CustomAccountFactory>();
- 授权
# add the following code in the page according to your need
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "admin")]
- 测试
我的页面
@page "/counter"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "admin")]
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private async Task IncrementCount()
{
currentCount++;
}
}
我的用户
结果