角色授权在 .NET 5.0 Blazor 客户端应用程序中不起作用
Authorize with roles is not working in .NET 5.0 Blazor Client app
我有一个 .NET 5.0 Blazor 客户端应用程序,但我无法让 [Authorize(Roles="Admin")]
和 AuthorizeView
标签工作。
我也有搭建身份页面:
我正在使用使用 Cosmos Db 的自定义身份实现:https://github.com/pierodetomi/efcore-identity-cosmos
我知道 Blazor 客户端项目模板中的角色授权是一个问题:https://github.com/dotnet/AspNetCore.Docs/issues/17649#issuecomment-612442543
我尝试了上面 Github 问题线程中提到的解决方法和以下 SO 答案:
...我仍然无法让它工作。
具有讽刺意味的是,IsInRoleAsync
方法在登录到应用程序后甚至没有被调用。我在自定义 CosmosUserStore
class 的实现上应用了一个断点,它没有被击中。
使用管理员用户登录应用程序后,浏览器控制台显示如下:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddCosmosIdentity<MyDbContext, IdentityUser, IdentityRole>(
// Auth provider standard configuration (e.g.: account confirmation, password requirements, etc.)
options => options.SignIn.RequireConfirmedAccount = true,
options => options.UseCosmos(
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
databaseName: "xxxxxxxxxxxxxxxxxxxxxxxxx"
),
addDefaultTokenProviders: true
).AddDefaultUI().AddRoles<IdentityRole>();
services.AddScoped<IUsersRepository, UsersRepository>();
services.AddIdentityServer().AddApiAuthorization<IdentityUser, MyDbContext>(options =>
{
options.IdentityResources["openid"].UserClaims.Add("role");
options.ApiResources.Single().UserClaims.Add("role");
});
// Need to do this as it maps "role" to ClaimTypes.Role and causes issues
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");
services.AddAuthentication()
.AddIdentityServerJwt();
services.AddControllersWithViews();
services.AddRazorPages();
}
Program.cs
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.Services.AddHttpClient("IdentityDocApp.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
// Supply HttpClient instances that include access tokens when making requests to the server project
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("IdentityDocApp.ServerAPI"));
builder.Services.AddHttpClient();
builder.Services.AddScoped<IManageUsersService, ManageUsersService>();
builder.Services.AddBlazorTable();
builder.Services.AddApiAuthorization();
builder.Services.AddApiAuthorization(options =>
{
options.UserOptions.RoleClaim = "role";
});
await builder.Build().RunAsync();
}
}
App.razor
NavMenu.razor
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<AuthorizeView Roles="Admin">
<li class="nav-item px-3">
<NavLink class="nav-link" href="users">
<span class="oi oi-person" aria-hidden="true"></span> Users
</NavLink>
</li>
</AuthorizeView>
</ul>
ManageUsers.razor
管理用户控制器
数据库在 UserRoles 集合中有正确的数据。没有问题。
那么,可能是什么问题?我做错了什么?
更新:
这很尴尬,但我在自定义用户存储中的 IsInRoleAsync
实现不正确。一旦我修复它,问题就消失了。
我只在服务器端的Startup.cs
中使用了以下代码:
services.AddIdentityServer()
.AddApiAuthorization<IdentityUser, MyDbContext>(options =>
{
options.IdentityResources["openid"].UserClaims.Add("name");
options.ApiResources.Single().UserClaims.Add("name");
options.IdentityResources["openid"].UserClaims.Add("role");
options.ApiResources.Single().UserClaims.Add("role");
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");
在client-side的Program.cs
中我只用了builder.Services.AddApiAuthorization();
感谢@MrC aka Shaun Curtis 让我知道问题出在 server-side。
将此粘贴到您的索引页面,以便您可以查看您的用户的信息:
@if (user is not null)
{
<h3>@user.Identity.Name</h3>
<div class="m-2 p-2">
Is Authenticated: @user.Identity.IsAuthenticated
</div>
<div class="m-2 p-2">
Authentication Type: @user.Identity.AuthenticationType
</div>
<div class="m-2 p-2">
Admin Role: @user.IsInRole("Admin")
</div>
<div class="m-2 p-2">
<h5>Claims</h5>
@foreach (var claim in user.Claims)
{
<span>
@claim.Type
</span>
<span>:</span>
<span>
@claim.Value
</span>
<br />
}
</div>
}
else
{
<div class="m-2 p-2">
No User Exists
</div>
}
@code {
[CascadingParameter] public Task<AuthenticationState> AuthTask { get; set; }
private System.Security.Claims.ClaimsPrincipal user;
protected async override Task OnInitializedAsync()
{
var authState = await AuthTask;
this.user = authState.User;
}
}
你应该得到这样的东西:
这显示了身份验证提供商在 header 中的身份验证数据中传递了哪些角色。这应该包括角色。
更新
删除:
// Need to do this as it maps "role" to ClaimTypes.Role and causes issues
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");
我有一个 .NET 5.0 Blazor 客户端应用程序,但我无法让 [Authorize(Roles="Admin")]
和 AuthorizeView
标签工作。
我也有搭建身份页面:
我正在使用使用 Cosmos Db 的自定义身份实现:https://github.com/pierodetomi/efcore-identity-cosmos
我知道 Blazor 客户端项目模板中的角色授权是一个问题:https://github.com/dotnet/AspNetCore.Docs/issues/17649#issuecomment-612442543
我尝试了上面 Github 问题线程中提到的解决方法和以下 SO 答案:
...我仍然无法让它工作。
具有讽刺意味的是,IsInRoleAsync
方法在登录到应用程序后甚至没有被调用。我在自定义 CosmosUserStore
class 的实现上应用了一个断点,它没有被击中。
使用管理员用户登录应用程序后,浏览器控制台显示如下:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddCosmosIdentity<MyDbContext, IdentityUser, IdentityRole>(
// Auth provider standard configuration (e.g.: account confirmation, password requirements, etc.)
options => options.SignIn.RequireConfirmedAccount = true,
options => options.UseCosmos(
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
databaseName: "xxxxxxxxxxxxxxxxxxxxxxxxx"
),
addDefaultTokenProviders: true
).AddDefaultUI().AddRoles<IdentityRole>();
services.AddScoped<IUsersRepository, UsersRepository>();
services.AddIdentityServer().AddApiAuthorization<IdentityUser, MyDbContext>(options =>
{
options.IdentityResources["openid"].UserClaims.Add("role");
options.ApiResources.Single().UserClaims.Add("role");
});
// Need to do this as it maps "role" to ClaimTypes.Role and causes issues
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");
services.AddAuthentication()
.AddIdentityServerJwt();
services.AddControllersWithViews();
services.AddRazorPages();
}
Program.cs
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.Services.AddHttpClient("IdentityDocApp.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
// Supply HttpClient instances that include access tokens when making requests to the server project
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("IdentityDocApp.ServerAPI"));
builder.Services.AddHttpClient();
builder.Services.AddScoped<IManageUsersService, ManageUsersService>();
builder.Services.AddBlazorTable();
builder.Services.AddApiAuthorization();
builder.Services.AddApiAuthorization(options =>
{
options.UserOptions.RoleClaim = "role";
});
await builder.Build().RunAsync();
}
}
App.razor
NavMenu.razor
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<AuthorizeView Roles="Admin">
<li class="nav-item px-3">
<NavLink class="nav-link" href="users">
<span class="oi oi-person" aria-hidden="true"></span> Users
</NavLink>
</li>
</AuthorizeView>
</ul>
ManageUsers.razor
管理用户控制器
数据库在 UserRoles 集合中有正确的数据。没有问题。
那么,可能是什么问题?我做错了什么?
更新:
这很尴尬,但我在自定义用户存储中的 IsInRoleAsync
实现不正确。一旦我修复它,问题就消失了。
我只在服务器端的Startup.cs
中使用了以下代码:
services.AddIdentityServer()
.AddApiAuthorization<IdentityUser, MyDbContext>(options =>
{
options.IdentityResources["openid"].UserClaims.Add("name");
options.ApiResources.Single().UserClaims.Add("name");
options.IdentityResources["openid"].UserClaims.Add("role");
options.ApiResources.Single().UserClaims.Add("role");
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");
在client-side的Program.cs
中我只用了builder.Services.AddApiAuthorization();
感谢@MrC aka Shaun Curtis 让我知道问题出在 server-side。
将此粘贴到您的索引页面,以便您可以查看您的用户的信息:
@if (user is not null)
{
<h3>@user.Identity.Name</h3>
<div class="m-2 p-2">
Is Authenticated: @user.Identity.IsAuthenticated
</div>
<div class="m-2 p-2">
Authentication Type: @user.Identity.AuthenticationType
</div>
<div class="m-2 p-2">
Admin Role: @user.IsInRole("Admin")
</div>
<div class="m-2 p-2">
<h5>Claims</h5>
@foreach (var claim in user.Claims)
{
<span>
@claim.Type
</span>
<span>:</span>
<span>
@claim.Value
</span>
<br />
}
</div>
}
else
{
<div class="m-2 p-2">
No User Exists
</div>
}
@code {
[CascadingParameter] public Task<AuthenticationState> AuthTask { get; set; }
private System.Security.Claims.ClaimsPrincipal user;
protected async override Task OnInitializedAsync()
{
var authState = await AuthTask;
this.user = authState.User;
}
}
你应该得到这样的东西:
这显示了身份验证提供商在 header 中的身份验证数据中传递了哪些角色。这应该包括角色。
更新
删除:
// Need to do this as it maps "role" to ClaimTypes.Role and causes issues
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");