角色授权在 .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");