通过AddIdentityServer向IdentityServer设置添加声明

时间:2019-05-23 20:16:29

标签: asp.net-core jwt identityserver4 oidc

我有一个SPA,它具有ASP.NET Core Web API以及使用AddIdentityServer然后AddIdentityServerJwt然后打开的内置身份服务器:

services.AddIdentityServer()
   .AddApiAuthorization<User, UserDataContext>();
services.AddAuthentication()
   .AddIdentityServerJwt();

我也有一个授权策略设置,需要一个“管理员”角色声明:

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

我有一个使用此策略的控制器操作

[Authorize(Policy = "IsAdmin")]
[HttpDelete("{id}")]
public IActionResult Deleten(int id)
{
    ...
}

经过身份验证的用户确实具有“管理员”角色声明:

enter image description here

此身份验证用户的访问令牌似乎不包含管理员声明:

enter image description here

尝试向管理员用户请求此资源时,我收到403返回:

enter image description here

因此,如果我正确理解这一点,则IdentityServer不包含管理员角色声明,因此用户无权访问该资源。

是否可以使用AddIdentityServerJwt配置IdentityServer使用的声明?还是我误解了为什么这不起作用。

3 个答案:

答案 0 :(得分:1)

在Identity Server方面,您可以创建Profile Service,以使IDS4在颁发令牌时包含role声明。

您可以从ClaimsPrincipal获取角色声明,或者从数据库获取角色并创建个人档案服务,例如:

public class MyProfileService : IProfileService
{
    public MyProfileService()
    { }

    public Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        //get role claims from ClaimsPrincipal 
        var roleClaims = context.Subject.FindAll(JwtClaimTypes.Role);

        //add your role claims 
        context.IssuedClaims.AddRange(roleClaims);
        return Task.CompletedTask;
    }

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

并在Startup.cs中注册:

services.AddTransient<IProfileService, MyProfileService>();

在客户端,您应该从JWT令牌映射角色声明,并尝试在AddOpenIdConnect中间件中的config下面进行操作:

  options.ClaimActions.MapJsonKey("role", "role", "role");
  options.TokenValidationParameters.RoleClaimType = "role";

然后您的api可以验证访问令牌并使用角色策略进行授权。

答案 1 :(得分:0)

我这样做的时候没有使用角色,而是使用了添加到用户令牌中的特殊声明。我创建了一个CustomUserClaimsPrincipalFactory,这使我可以向用户添加其他声明。

注册

services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, CustomUserClaimsPrincipalFactory>();

代码。

public class CustomUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole<long>>
    {
        public CustomUserClaimsPrincipalFactory(
            UserManager<ApplicationUser> userManager,
            RoleManager<IdentityRole<long>> roleManager,
            IOptions<IdentityOptions> optionsAccessor)
            : base(userManager, roleManager, optionsAccessor)
        {
        }

        protected override async Task<ClaimsIdentity> GenerateClaimsAsync(ApplicationUser user)
        {  
            var userId = await UserManager.GetUserIdAsync(user);
            var userName = await UserManager.GetUserNameAsync(user);
            var id = new ClaimsIdentity("Identity.Application", 
                Options.ClaimsIdentity.UserNameClaimType,
                Options.ClaimsIdentity.RoleClaimType);
            id.AddClaim(new Claim(Options.ClaimsIdentity.UserIdClaimType, userId));
            id.AddClaim(new Claim(Options.ClaimsIdentity.UserNameClaimType, user.Name));
            id.AddClaim(new Claim("preferred_username", userName));
            id.AddClaim(new Claim("culture", user.Culture ?? "da-DK"));
            if (UserManager.SupportsUserSecurityStamp)
            {
                id.AddClaim(new Claim(Options.ClaimsIdentity.SecurityStampClaimType,
                    await UserManager.GetSecurityStampAsync(user)));
            }
            if (UserManager.SupportsUserClaim)
            {
                id.AddClaims(await UserManager.GetClaimsAsync(user));
            }

            if(user.IsXenaSupporter)
               id.AddClaim(new Claim("supporter", user.Id.ToString()));
            return id;
        }
    }

政策

services.AddAuthorization(options =>
            {
                options.AddPolicy("Supporter", policy => policy.RequireClaim("supporter"));
            });

用法

    [Authorize(AuthenticationSchemes = "Bearer", Policy = "Supporter")]
    [HttpPost("supporter")]
    public async Task<ActionResult> ChangeToSpecificUser([FromBody] ChangeUserRequest request)
    {

     // .................. 

    }

答案 2 :(得分:0)

其他答案之一确实与所讨论的特定用例非常接近,但是没有找到关于SPA的要点。

首先,您必须像建议的那样添加IProfileService实现:

public class MyProfileService : IProfileService
{
    public MyProfileService()
    { }

    public Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        //get role claims from ClaimsPrincipal 
        var roleClaims = context.Subject.FindAll(JwtClaimTypes.Role);

        //add your role claims 
        context.IssuedClaims.AddRange(roleClaims);
        return Task.CompletedTask;
    }

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

但是请继续执行以下操作:

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

您的主张将在JWT上公开。将 ClaimTypes.Role 常量替换为与要公开的声明类型相对应的任何字符串。