使用 SPA 和 .NET Core 3 识别角色

Identify roles with SPA and .NET Core 3

我有一个使用 .NET Core 3.1 的应用程序,还有一个使用默认 React 应用程序的前端,从这个 link 生成。

在 .NET Core 应用程序中,我使用用户和角色设置了 Identity Server。

当我在 React 应用程序中时,我想知道用户的角色。我看到目前正在使用一个名为 oidc-client 的库。

从授权用户时我可以调试的响应中,我看到返回了一些范围。

scope: "openid profile [Name of the app]"

这是完整的回复。

我如何知道该用户的角色? 我是否需要将它添加到我的 .NET Core 应用程序中的某处? 或者我可以从响应中的 access_token 中计算出来吗?

该模板使用 ASP.NET Core Identity 来管理 users/roles。所以第一件事就是启用角色:

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

创建自定义配置文件服务以将自定义声明包含到令牌和用户信息端点中:

public class ProfileService : IProfileService
{
    protected readonly UserManager<ApplicationUser> _userManager;


    public ProfileService(UserManager<ApplicationUser> userManager)
    {
        _userManager = userManager;
    }

    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        ApplicationUser user = await _userManager.GetUserAsync(context.Subject);

        IList<string> roles = await _userManager.GetRolesAsync(user);

        IList<Claim> roleClaims = new List<Claim>();
        foreach (string role in roles)
        {
            roleClaims.Add(new Claim(JwtClaimTypes.Role, role));
        }

        //add user claims

        roleClaims.Add(new Claim(JwtClaimTypes.Name, user.UserName));
        context.IssuedClaims.AddRange(roleClaims);
    }

    public Task IsActiveAsync(IsActiveContext context)
    {
        return Task.CompletedTask;
    }
}

并在Startup.cs注册:

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

现在声明将包含在 userinfo 端点中,您的 React 应用程序将自动请求 userinfo 端点以获取 AuthorizeService.js 文件的 getUser 函数中的用户配置文件,跟踪 _user.profile 到得到新的索赔。此外,角色声明包含在访问令牌中。

您不必实施 ProfileService。 ReactJS+ID4模板已经为前端设置了一个客户端(Client[0]),你只需要添加适当的配置,让它把角色放入令牌中。

        services
            .AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
            .AddRoles<IdentityRole>() //<- Very important, don't forget
            .AddEntityFrameworkStores<AuthDbContext>();

        services.AddIdentityServer()
            .AddApiAuthorization<ApplicationUser, AuthDbContext>(x =>
            {
                x.IdentityResources.Add(new IdentityResource("roles", "Roles", new[] { JwtClaimTypes.Role, ClaimTypes.Role }));
                foreach(var c in x.Clients)
                {
                    c.AllowedScopes.Add("roles");
                }
                foreach (var a in x.ApiResources)
                {
                    a.UserClaims.Add(JwtClaimTypes.Role);
                }
            });

在客户端,谨慎使用角色。根据分配给用户的角色数量,它可以是字符串或字符串数​​组。我使用 ensureArray 函数来帮助解决这个问题。

  isAdmin(user: User|null): boolean {
    return this.isInAnyRole(user, ["Admin"]);
  }

  isInAnyRole(user: User|null, requiredAnyRoles: string[]): boolean {
    var authorized = false;
    if (user) {
      var userRoles = this.ensureArray(user.profile.role);
      requiredAnyRoles.forEach(role => {
        if (userRoles.indexOf(role) > -1) {
          authorized = true;
        }
      });
    }
    return authorized;
  }

  private ensureArray(value: any): string[] {
    if (!Array.isArray(value)) {
      return [<string>value];
    }
    return value;
  }

然后您可以在您的服务器端添加策略。

services.AddAuthorization(options =>
{
     options.AddPolicy("RequireAdminRole", policy =>
     {
          policy.RequireClaim(ClaimTypes.Role, "Admin");
     });
});

保护你的api

[Authorize(Policy = "RequireAdminRole")]
[HttpPost()]
public async Task<IActionResult> Post([FromBody] CreateModel model)