【问题标题】:IdentityServer4 + JWT: Define user property for "sub" claimIdentityServer4 + JWT:为“sub”声明定义用户属性
【发布时间】:2018-08-27 15:39:03
【问题描述】:

我将 IdentityServer4 与 JWT 和 ASP.NET Core 2 一起用于身份验证。由于大查询的性能问题,我还使用IdentityUser<long>long 主键而不是string)作为用户模型。这意味着我在编码的访问令牌内获得了一个有序的用户 ID,默认情况下该用户 ID 将提供给公共。使用 GUID 作为默认类型是有原因的,因此我只想向公众提供 GUID,而不是有序整数。为此,我有一个额外的 GUID 类型的 ExternalID 列,将在创建用户时生成一次。

现在我需要替换 sub 声明,其中默认情况下整数 Id 与我的 ExternalId (GUID) 一起使用。

我尝试使用ProfileService 和以下GetProfileDataAsync 方法

public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
    var sub = context.Subject.GetSubjectId();
    var user = await _userManager.FindByIdAsync(sub);
    var principal = await _claimsFactory.CreateAsync(user);

    var claims = principal.Claims.Where(
        claim => 
        claim.Type != "sub" && 
        context.RequestedClaimTypes.Contains(claim.Type))
        .ToList();

    claims.Add(new Claim("sub", user.ExternalId.ToString()));

    context.IssuedClaims = claims;
}

Startup.cs

services.AddIdentityServer()
   // ...
   .AddProfileService<ProfileService>();

但这没有任何效果。 IssuedClaims 似乎被忽略了。令牌仍然包含 Id sub 声明而不是 ExternalId

我错过了什么?这是正确的方法吗?

【问题讨论】:

  • 只是为了确定 - 您是否确认您的 GetProfileDataAsync() 方法正在被调用?
  • 是的,肯定是在用户第一次登录的时候调用的。
  • 刷新令牌交换没有调用。
  • 我能够通过扩展 Microsoft.AspNetCore.Identity 包中的 UserClaimsPrincipleFactory 来完成这项工作。至少在大多数情况下。当通过首先点击 ResourceOwnerPassword 端点,然后是 UserInfo 端点来获取 id_token 时,我会收到一个禁止的错误。 access_token 的 sub 声明仍显示 int 值,而 id_token 显示 GUID。冲突导致错误。当使用混合流通过 oidc 获取访问令牌时,子声明将正确返回。如果你能解决这个问题,我很想听听!

标签: c# asp.net-core jwt identityserver4


【解决方案1】:

尽管我在上面发表了评论,但我还是能够完成这项工作,并且没有看到任何副作用。至少我还看不到。您将需要在两个位置替换子声明。

首先像这样扩展 Microsoft.AspNetCore.Identity.UserClaimsPrincipleFactory。这将在收到 Id 令牌之前修改声明。

public sealed class MyUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, ApplicationRole>
{
    public MyUserClaimsPrincipalFactory(UserManager userManager, RoleManager<ApplicationRole> roleManager, IOptions<IdentityOptions> optionsAccessor)
            : base(userManager, roleManager, optionsAccessor)
    {}   

    protected override async Task<ClaimsIdentity> GenerateClaimsAsync(ApplicationUser user)
    { 
        var identity = await base.GenerateClaimsAsync(user);
        var sub = identity.FindFirst("sub");
        if (sub == null)
            return identity; 

        identity.RemoveClaim(sub);
        identity.AddClaim(new Claim("sub", user.GlobalUid.ToString()));

        return identity;
    }
}

第二个扩展 IdentityServer4.Services.DefaultClaimsService 像这样。这将解决我在上面的 cmets 中提到的问题。

public class MyClaimsService : DefaultClaimsService
{
    private readonly UserManager _userManager;

    public MyClaimsService(IProfileService profile, ILogger<DefaultClaimsService> logger, UserManager userManager) : base(profile, logger)
    {
        _userManager = userManager;
    }

    protected override IEnumerable<Claim> GetStandardSubjectClaims(ClaimsPrincipal subject)
    {
        var sub = subject.GetSubjectId();
        var user = _userManager.FindByIdAsync(sub).GetAwaiter().GetResult();
        var claims = new List<Claim>
        {
            new Claim(JwtClaimTypes.Subject, user?.GlobalUid.ToString() ?? sub),
            new Claim(JwtClaimTypes.AuthenticationTime, subject.GetAuthenticationTimeEpoch().ToString(), ClaimValueTypes.Integer),
            new Claim(JwtClaimTypes.IdentityProvider, subject.GetIdentityProvider())
        };

        claims.AddRange(subject.GetAuthenticationMethods());

        return claims;
    }
}

在添加 Microsoft.AspNetCore.Identity 时通过身份生成器将 UserClaimsPrincipleFactory 添加到服务集合中

 services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
     {
         options.User.RequireUniqueEmail = true;
         options.SignIn.RequireConfirmedEmail = true;
     })
     .AddClaimsPrincipalFactory<MyUserClaimsPrincipalFactory>() 

然后像这样将 ClaimsService 添加为瞬态

services.AddTransient<IClaimsService, MyClaimsService>();    

现在您的所有声明都应作为您为所有令牌提供的属性返回。如果您对此有任何问题,请告诉我,因为它对我来说仍处于测试阶段。

【讨论】:

  • 感谢您的回答!其实我没有时间测试这个。但我稍后会对此进行测试,如果可行,我将标记答案。
  • 没问题。就像我说的,我也没有太多的测试时间,但这似乎有效。让我知道进展如何,或者您是否找到更好的方法!
  • 你有没有得到这个工作?如果您有请投票并标记为正确。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-11-20
  • 2018-07-05
  • 2018-02-08
  • 2019-02-09
  • 2017-10-09
  • 2017-05-30
  • 2020-11-25
相关资源
最近更新 更多