如何在IdentityServer4中向访问令牌添加自定义声明?

81

我正在使用IdentityServer4

我想要在访问令牌中添加其他自定义声明,但是我无法做到这一点。我已经修改了Quickstart5,并通过ProfileService添加了ASP.NET Identity Core和自定义声明,正如Coemgen在下面建议的那样。

您可以在这里下载我的代码:[zip包][3]。(基于:Quickstart5,使用了ASP.NET Identity Core并通过ProfileService添加了声明)。

问题:GetProfileDataAsync没有执行。


我对这个问题感到困惑,001。你显然是一个非常有经验的Stack Overflow用户,但你把你的代码放在了文件锁中而不是问题中,因此它是不相关的 - 你必须知道这一点。你能修复这个问题,使它不被暂停吗? - halfer
4个回答

124
你应该自己实现你的ProfileService。 看一下这篇帖子,我在实现相同功能时参考了它:

https://damienbod.com/2016/11/18/extending-identity-in-identityserver4-to-manage-users-in-asp-net-core/

这是我自己实现的一个例子:
public class ProfileService : IProfileService
{
    protected UserManager<ApplicationUser> _userManager;

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

    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        //>Processing
        var user = await _userManager.GetUserAsync(context.Subject);

        var claims = new List<Claim>
        {
            new Claim("FullName", user.FullName),
        };

        context.IssuedClaims.AddRange(claims);
    }

    public async Task IsActiveAsync(IsActiveContext context)
    {
        //>Processing
        var user = await _userManager.GetUserAsync(context.Subject);
        
        context.IsActive = (user != null) && user.IsActive;
    }
}

不要忘记在你的 Startup.cs 中配置服务(通过这个答案)。
services.AddIdentityServer()
    .AddProfileService<ProfileService>();

5
谢谢你的回复,但它仍然没有起作用!没有添加任何索赔! - 001
好的,但是如果您添加了断点,您是否针对 GetProfileDataAsync 进行了调试? - KJBTech
1
在启动时,它执行此“services.AddTransient<IProfileService,ProfileService>(); //AddClaims”,但是它断点,不执行GetProfileDataAsync方法。 - 001
让我们在聊天中继续这个讨论。点击此处进入聊天室 - 001
这种信息居然没有在实际文档中存在,真让人费解!谢谢! - undefined
显示剩余11条评论

57
好的,这里的问题是这样的:
尽管您已经正确配置了可用的身份资源(包括标准和自定义资源),但您还需要明确地定义在调用您的API资源时哪些资源是必需的。为了定义这一点,您需要进入您的“Config.cs”类,在“ExampleIdentityServer”项目中提供第三个参数,就像在“new ApiResource”构造函数中一样。只有这些资源将被包含在“access_token”中。
// scopes define the API resources in your system
public static IEnumerable<ApiResource> GetApiResources()
{
    return new List<ApiResource>
    {
        new ApiResource("api1", "My API", new[] { JwtClaimTypes.Subject, JwtClaimTypes.Email, JwtClaimTypes.Phone, etc... })
    };
}

本质上,这意味着我已经为我的组织配置了身份声明,但可能涉及多个API,并且并非所有API都使用所有可用的个人资料声明。这也意味着这些声明将存在于您的ClaimsPrincipal中,其他所有内容仍然可以通过“userinfo”端点作为普通的HTTP调用来访问。
注意:关于刷新令牌:
如果您选择通过AllowOfflineAccess = true启用刷新令牌,刷新访问令牌时可能会遇到相同的行为“GetProfileDataAsync不执行!”。因此,访问令牌中的声明保持不变,尽管您获得了具有更新生命周期的新访问令牌。如果是这种情况,您可以通过在客户端配置中设置UpdateAccessTokenClaimsOnRefresh=true来强制始终从个人资料服务刷新它们。

46

发现问题。

在 startup.cs 中,不要添加 services.AddTransient<IProfileService, ProfileService>();,而是添加 .AddProfileService<ProfileService>()services.AddIdentityServer() 中。

最后结果将会如下:

services.AddIdentityServer()
    .AddTemporarySigningCredential()
    .AddInMemoryIdentityResources(Config.GetIdentityResources())
    .AddInMemoryApiResources(Config.GetApiResources())
    .AddInMemoryClients(Config.GetClients())
    .AddAspNetIdentity<ApplicationUser>()
    .AddProfileService<ProfileService>();

感谢Coemgen的帮助!代码没有问题,只是启动方式不对。


2
有趣的是,您还应该能够使用 services.AddTransient<IProfileService,ProfileService>();。 - KJBTech
4
在 Microsoft 架构 GitHub 存储库上有一个很好的例子:https://github.com/dotnet-architecture/eShopOnContainers/blob/master/src/Services/Identity/Identity.API/Startup.cs - AdrienTorris
2
@Coemgen 你也可以这样做!但是你必须在 "services.AddIdentityServer()" 后面添加 " services.AddTransient<IProfileService, ProfileService>();" :) - 001
1
我认为如果你想坚持使用 services.AddTransient<IProfileService, ProfileService>();,那么在将 IdentityServer 添加到服务后,应该这样做,这样你的注册将覆盖 IdentityServer 所做的注册。 - pushist1y
1
对于任何阅读此内容的人来说,顺序很重要,profileService 应该最后注册。 - kennydust
显示剩余2条评论

1

1
我试过了,不起作用! - 001
1
我按照这个做了,但是不起作用!http://docs.identityserver.io/en/release/topics/resources.html#defining-identity-resources - 001

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接