【问题标题】:Identify roles with SPA and .NET Core 3使用 SPA 和 .NET Core 3 识别角色
【发布时间】:2020-07-26 19:12:32
【问题描述】:

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

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

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

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

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

这是完整的回复。

我如何知道该用户的角色? 我需要将它添加到我的 .NET Core 应用程序中的某个位置吗? 或者我可以从回复中的access_token 算出来吗?

【问题讨论】:

    标签: reactjs asp.net-core .net-core single-page-application .net-core-3.0


    【解决方案1】:

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

    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 以获取新声明。此外,角色声明包含在访问令牌中。

    【讨论】:

    • 如果我在 JWT 令牌中拥有角色为“Admin”的角色并且我的 [Authorize(Roles="Admin")] 端点给了我 403,我该怎么办? :
    【解决方案2】:

    您不必实现 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)
    

    【讨论】:

      猜你喜欢
      • 2021-08-10
      • 1970-01-01
      • 2019-05-07
      • 1970-01-01
      • 2020-04-03
      • 2020-01-29
      • 1970-01-01
      • 2019-08-05
      • 1970-01-01
      相关资源
      最近更新 更多