Asp.net core 如何使用ASP.Net Identity with IdentityServer 4添加要包含在access_令牌中的其他声明

Asp.net core 如何使用ASP.Net Identity with IdentityServer 4添加要包含在access_令牌中的其他声明,asp.net-core,identityserver4,asp.net-core-webapi,asp.net-core-identity,Asp.net Core,Identityserver4,Asp.net Core Webapi,Asp.net Core Identity,如何添加要包含在令牌中的其他声明 一旦API接收到承载令牌,User.Identity对象就会填充以下声明 [ { "key": "nbf", "value": "1484614344" }, { "key": "exp", "value": "1484615244" }, { "key": "iss", "value": "http://localhost:85" }, { "key": "aud",

如何添加要包含在令牌中的其他声明

一旦API接收到承载令牌,User.Identity对象就会填充以下声明

[
  {
    "key": "nbf",
    "value": "1484614344"
  },
  {
    "key": "exp",
    "value": "1484615244"
  },
  {
    "key": "iss",
    "value": "http://localhost:85"
  },
  {
    "key": "aud",
    "value": "http://localhost:85/resources"
  },
  {
    "key": "aud",
    "value": "WebAPI"
  },
  {
    "key": "client_id",
    "value": "MyClient"
  },
  {
    "key": "sub",
    "value": "d74c815a-7ed3-4671-b4e4-faceb0854bf6"
  },
  {
    "key": "auth_time",
    "value": "1484611732"
  },
  {
    "key": "idp",
    "value": "local"
  },
  {
    "key": "role",
    "value": "AccountsManager"
  },
  {
    "key": "scope",
    "value": "openid"
  },
  {
    "key": "scope",
    "value": "profile"
  },
  {
    "key": "scope",
    "value": "roles"
  },
  {
    "key": "scope",
    "value": "WebAPI"
  },
  {
    "key": "scope",
    "value": "offline_access"
  },
  {
    "key": "amr",
    "value": "pwd"
  }
]
我需要其他声明,如
用户名、电子邮件、legacySystemUserId
等。这些字段已经存在于
AspNetUsers
表中(并且在
AspNetUserClaims
表中不重复存在),并且在我的ApplicationUser对象的ASP.Net核心应用程序中可用


我希望它们包含在访问令牌中,在使用用户名和密码进行身份验证后返回。希望在我的WebAPI应用程序中使用相同的方法,该应用程序没有访问identity server数据库的权限,并且它自己的数据库存储的数据基于用户的电子邮件地址,而不是用户ID(它是在ASP.NET identity中生成的guid,并作为子声明接收).

我为同一个问题奋斗了几个小时,终于拼凑出了解决方案。这是一个很大的帮助,但要总结和分享我的实现:

为了获得分配给用户的声明并将其附加到访问令牌,您需要在identity server上实现两个接口:
IResourceOwnerPasswordValidator
IProfileService
。下面是我对这两个类的实现,它们是粗略的草稿,但它们可以工作

**此时请确保获取最新版本的IdentityServer4-1.0.2

公共类ResourceOwnerPasswordValidator:IResourceOwnerPasswordValidator
{
私有只读用户管理器_UserManager;
公共资源所有者PasswordValidator(UserManager UserManager)
{
_userManager=userManager;
}
公共任务ValidateSync(ResourceOwnerPasswordValidationContext)
{
var userTask=\u userManager.FindByNameAsync(context.UserName);
var user=userTask.Result;
context.Result=new GrantValidationResult(user.Id,“password”,null,“local”,null);
返回Task.FromResult(context.Result);
}
}

公共类AspNetIdentityProfileService:IProfileService
{
私有只读用户管理器_UserManager;
公共AspNetIdentityProfileService(UserManager UserManager)
{
_userManager=userManager;
}
公共异步任务GetProfileDataAsync(ProfileDataRequestContext上下文)
{
var subject=context.subject;
如果(subject==null)抛出新的ArgumentNullException(nameof(context.subject));
var subjectId=subject.GetSubjectId();
var user=await\u userManager.FindByIdAsync(subjectId);
if(user==null)
抛出新ArgumentException(“无效的主题标识符”);
var索赔=等待GetClaimsFromUser(用户);
var siteIdClaim=claims.SingleOrDefault(x=>x.Type==”http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress");
添加(新索赔(JwtClaimTypes.Email,user.Email));
添加(新索赔(“siteid”,siteIdClaim.Value));
context.IssuedClaims.Add(新索赔(JwtClaimTypes.Role,“User”);
var roleClaims=索赔。其中(x=>x.Type==”http://schemas.microsoft.com/ws/2008/06/identity/claims/role");
foreach(roleClaims中的var roleClaim)
{
context.IssuedClaims.Add(新索赔(JwtClaimTypes.Role,roleClaim.Value));
}
}
公共异步任务IsActiveAsync(IsActiveContext上下文)
{
var subject=context.subject;
如果(subject==null)抛出新的ArgumentNullException(nameof(context.subject));
var subjectId=subject.GetSubjectId();
var user=await\u userManager.FindByIdAsync(subjectId);
context.IsActive=false;
如果(用户!=null)
{
if(_userManager.SupportsUserSecurityStamp)
{
var security_stamp=subject.Claims.Where(c=>c.Type==“security_stamp”)。选择(c=>c.Value.SingleOrDefault();
如果(安全戳!=null)
{
var db_security_stamp=wait_userManager.GetSecurityStampAsync(用户);
if(db_安全戳!=安全戳)
返回;
}
}
context.IsActive=
!user.LockoutEnabled||
!user.LockoutEnd.HasValue||
user.lockouten新声明(JwtClaimTypes.Role,Role));
}
退货索赔;
}
}
一旦您拥有这些功能,就需要将它们添加到startup.cs中的服务中:

services.AddTransient();
services.AddTransient();
下面快速查看我的配置:

公共静态IEnumerable GetIdentityResources()
{
返回新列表
{
新的IdentityResources.OpenId()
};
}
公共静态IEnumerable GetApiResources()
{
返回新列表
{
新资源
{
Name=“api1”,
Description=“我的Api”,
范围=
{
新范围()
{
Name=“api1”,
DisplayName=“完全访问Api”
}
}
}
};
}
公共静态IEnumerable GetClients()
{
返回新列表
{
新客户
{
ClientId=“apiClient”,
ClientName=“Api Angular2客户端”,
AllowedGrantTypes=GrantTypes.ResourceOwnerPassword,
AlwaysSendClientClaims=true,
AlwaysIncludeUserClaimsInIdToken=真,
客户秘密=
{
新密码(“Secret.Sha256())
},
允许范围=
{
“api1”
}
}
};
}
之后,从客户端调用identity server:

var discoTask=DiscoveryClient.GetAsync(“http://localhost:5000");
var disco=磁盘