基于Blazor身份验证角色

时间:2020-06-10 14:57:58

标签: authentication blazor roles

我正在使用最新的Webassembly版本(3.2.0)开发客户端blazor应用程序。

我从视觉工具开始了该项目,启用了本地身份验证,然后尝试添加角色。

首先,我在ApplicationDbContext中添加了角色:

public class ApplicationDbContext : ApiAuthorizationDbContext<ApplicationUser>
{
    public ApplicationDbContext(
        DbContextOptions options,
        IOptions<OperationalStoreOptions> operationalStoreOptions) : base(options, operationalStoreOptions)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.Entity<IdentityRole>()
               .HasData(new IdentityRole { Name = "User", NormalizedName = "USER", Id = Guid.NewGuid().ToString(), ConcurrencyStamp = Guid.NewGuid().ToString() });
        builder.Entity<IdentityRole>()
               .HasData(new IdentityRole { Name = "Admin", NormalizedName = "ADMIN", Id = Guid.NewGuid().ToString(), ConcurrencyStamp = Guid.NewGuid().ToString() });
    }
}

然后我在启动类中将角色添加到IdentityBuilder中:

public void ConfigureServices(IServiceCollection services)
{
   services.AddDbContext<ApplicationDbContext>(options =>
       options.UseSqlServer(
           Configuration.GetConnectionString("DefaultConnection")));

   services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
       .AddRoles<IdentityRole>()
       .AddEntityFrameworkStores<ApplicationDbContext>();

   services.AddIdentityServer()
       .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();

   services.AddAuthentication()
       .AddIdentityServerJwt();

   services.AddControllersWithViews();
   services.AddRazorPages();
}

然后在我的DbInitializer中创建一个同时具有两个角色的Admin帐户:

    private async Task SeedASPIdentityCoreAsync()
    {
        if (!await context.Users.AnyAsync())
        {
            var admin = new ApplicationUser()
            {
                UserName = "admin@admin.com",
                Email = "admin@admin.com",
                EmailConfirmed = true,
            };
            var result = await userManager.CreateAsync(admin, "aA&123");
            if (!result.Succeeded)
            {
                throw new Exception(result.Errors.First().Description);
            }

            result = await userManager.AddClaimsAsync(admin, new Claim[]
                {
                    new Claim(JwtClaimTypes.Email, "admin@admin.com"),
                    new Claim(JwtClaimTypes.Name, "admin@admin.com")
                });


            ApplicationUser user = await userManager.FindByNameAsync("admin@admin.com");

            try
            {
                result = await userManager.AddToRoleAsync(user, "User");
                result = await userManager.AddToRoleAsync(user, "Admin");
            }
            catch
            {
                await userManager.DeleteAsync(user);
                throw;
            }

            if (!result.Succeeded)
            {
                await userManager.DeleteAsync(user);
                throw new Exception(result.Errors.First().Description);
            }
        }
    }

但是角色没有出现在JWT中,并且客户端也不知道角色。

我如何在JWT中添加角色,就像新版本的blazor一样,不需要LoginController? (如果我很了解这些变化)

1 个答案:

答案 0 :(得分:0)

好,我找到了我需要的东西:

1)在客户端应用中创建CustomUserFactory

using System.Linq;
using System.Security.Claims;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;

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);

        if (user.Identity.IsAuthenticated)
        {
            var identity = (ClaimsIdentity)user.Identity;
            var roleClaims = identity.FindAll(identity.RoleClaimType);

            if (roleClaims != null && roleClaims.Any())
            {
                foreach (var existingClaim in roleClaims)
                {
                    identity.RemoveClaim(existingClaim);
                }

                var rolesElem = account.AdditionalProperties[identity.RoleClaimType];

                if (rolesElem is JsonElement roles)
                {
                    if (roles.ValueKind == JsonValueKind.Array)
                    {
                        foreach (var role in roles.EnumerateArray())
                        {
                            identity.AddClaim(new Claim(options.RoleClaim, role.GetString()));
                        }
                    }
                    else
                    {
                        identity.AddClaim(new Claim(options.RoleClaim, roles.GetString()));
                    }
                }
            }
        }

        return user;
    }
}

2)注册客户端工厂

builder.Services.AddApiAuthorization()
.AddAccountClaimsPrincipalFactory<CustomUserFactory>();

3)在服务器应用中,调用IdentityBuilder.AddRoles

services.AddDefaultIdentity<ApplicationUser>(options => 
options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();

4)配置身份服务器

services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(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");

还有另一种方法,通过创建ProfileService

5)用户授权机制:

<AuthorizeView Roles="admin">

来源:https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/security/blazor/webassembly/hosted-with-identity-server.md#Name-and-role-claim-with-API-authorization