【问题标题】:How can I make a custom claim accessible inside a webapi secured by Identity Server 4?如何在 Identity Server 4 保护的 webapi 中访问自定义声明?
【发布时间】:2021-02-16 16:35:47
【问题描述】:

我有一个 asp.net core mvc web 应用程序和一个使用 IdentityServer4 保护的 asp.net core api 项目。 当用户登录(到 mvc 应用程序)时,我添加了一个声明(“mycompany”),我希望它可以在 api 项目中访问。

我可以访问mvc项目中的附加声明没问题,但我无法访问api项目中的新声明。

如何使声明(“mycompany”)显示在 api 项目中?

MVC 应用启动.cs

services.AddAuthentication(options =>
                {
                    options.DefaultScheme = "Cookies";
                    options.DefaultChallengeScheme = "oidc";
                })
                .AddCookie("Cookies").AddOpenIdConnect("oidc", options =>
                {
                    options.Authority = Configuration.GetSection("IdpConfig").GetValue<string>("IdpUri");
                    options.RequireHttpsMetadata = false;

                    options.ClientId = "MyProject.Web";
                    options.ClientSecret = "SomeSecret";
                    options.ResponseType = "code id_token";
                    options.SaveTokens = true;

                    options.Events = new OpenIdConnectEvents
                    {
                        OnTicketReceived = async (context) =>
                        {
                            try
                            {
                                var claimsIdentity = context.Principal.Identity as ClaimsIdentity;

                                claimsIdentity.AddClaim(new Claim("mycompany", "some company"));

                                await Task.FromResult(0);
                            }
                            catch (Exception ex)
                            {
                                // Log This
                            }
                        },
                        OnUserInformationReceived = (context) => Task.FromResult(0),
                        OnTokenValidated = (context) =>
                        {
                            return Task.FromResult(0);
                        },
                        OnRedirectToIdentityProvider = (context) => Task.CompletedTask
                    };
                });

在 MVC 应用程序中这有效(返回“某些公司”),但在 api 中声明不存在

public static string GetCompany(this ClaimsPrincipal principal)
{
    var company = principal.Claims.FirstOrDefault(c => c.Type == "mycompany");
    return company?.Value;
}

【问题讨论】:

    标签: asp.net asp.net-mvc asp.net-core asp.net-web-api identityserver4


    【解决方案1】:

    他们声称您在 MVC 应用程序中添加的内容已添加到本地身份,您需要将新的声明添加到您发送到 API 的访问令牌中。您可以将该声明添加到 IdentitySever 中的 APIScope/APIResources。您无法更改 MVC 应用程序中的访问权限,因为令牌的签名将不再正确。

    【讨论】:

    • 感谢您的帮助。在对身份服务器进行身份验证后,我使用用户 ID 在不同的数据库(mvc 应用程序数据库)中查找有关用户的数据......这个新数据是我想要添加为自定义声明的内容(供 api 使用)。身份服务器无权访问 mvc app 数据库,那么如何将声明添加到 apiscope/apiresource?在 MVC 应用程序中收到访问令牌后,我似乎只能选择添加声明?
    • 访问令牌是自包含的,在不破坏其有效性的情况下无法更改。在我看来,您有 2 个选项。 1. 在您的身份服务器上移动用户数据及其声明,并在创建令牌时在您的令牌中添加声明。 2. 在您的 mvc 应用程序中创建端点并调用表单 api 以获取您想要的数据。我个人会选择选项 1,因为我认为将所有用户(甚至公司)相关数据放在集中式身份服务器上会更好。
    • 更好的是让 API 查找必要的详细信息,作为 API 中授权步骤的一部分,并让访问令牌仅识别用户。
    • 我喜欢“专门的授权 API 方法”。如果不适用,另一种选择是在 MVC 中公开 API,从 IdSrv 调用它并在其中添加所有广泛必要的声明。
    【解决方案2】:

    我认为它的工作方式

    在身份服务器上,您可以为用户(AspNetUserClaims 表)添加新的声明,并为其命名公司和值 TEST COMPANY。 在 IdentityClaims 表中添加声明公司并将该记录引用到配置文件范围的 IdentityResourceId(IdentityResources 表),因此当您的客户端应用程序请求配置文件范围时,IS 将使用公司声明及其值填充 AccessToken。

    配置 MVC 客户端以映射自定义声明并能够询问所需的范围

    options.ClaimActions.MapUniqueJsonKey("MyCompany", "company");
    
    //two trips to load claims into the cookie
    //but the id token is smaller
    options.GetClaimsFromUserInfoEndpoint = true;
    
    //configure scope
    options.Scope.Clear();
    options.Scope.Add("openid");
    options.Scope.Add("profile");
    

    如果您获得访问令牌并在 JWT.IO 上检查它,则应包含自定义声明公司及其值。

    var accessToken = await HttpContext.GetTokenAsync("access_token");
    

    在您的 MVC 客户端中,您可以获得这样的公司名称:

    var companyName = User.Claims.Where(x => x.Type == "MyCompany").FirstOrDefault()?.Value;
    

    【讨论】:

      【解决方案3】:

      要在您的 API 项目中访问自定义声明,您需要将这些声明包含在您的 JWT 访问令牌中。对于IdentityServer4,需要实现IProfileService接口。

      例如

      public class UserProfileService : IProfileService
      {
          public Task GetProfileDataAsync(ProfileDataRequestContext context)
          {
              // Ensure adding claims to access token only
              if (context.Caller.Equals("ClaimsProviderAccessToken", StringComparison.OrdinalIgnoreCase))
              {
                  var claims = new List<Claim>();
      
                  claims.Add(new Claim("<CUSTOM_CLAIM_TYPE>", "value", ClaimValueTypes.String));
                  
                  context.RequestedClaimTypes = new[] { "<CUSTOM_CLAIM_TYPE>" };
      
                  context.AddRequestedClaims(claims);
              }
      
              return Task.CompletedTask;
          }
      
          public Task IsActiveAsync(IsActiveContext context)
          {
              context.IsActive = true;
      
              return Task.CompletedTask;
          }
      }
      

      您还可以使用IUserClaimsPrincipalFactory 向身份添加自定义声明,然后在GetProfileDataAsync() 方法中通过RequestedClaimTypes 过滤它们。

      然后在Startup类的ConfigureServices()方法中,将UserProfileService注册到IIdentityServerBuilder

      services.AddIdentityServer(...)
          .AddProfileService<UserProfileService>();
      

      现在,您的访问令牌具有自定义声明,您可以在 API 项目中访问这些声明,例如,通过控制器的 User 属性。

      正如 cmets 中已经提到的,建议尽可能保持访问令牌的轻量级,并仅在需要时根据访问令牌中的用户 ID 获取自定义声明。 p>

      【讨论】:

        猜你喜欢
        • 2019-12-28
        • 2018-10-06
        • 1970-01-01
        • 2017-05-14
        • 2020-01-06
        • 1970-01-01
        • 2021-10-01
        • 2020-02-06
        • 2020-02-10
        相关资源
        最近更新 更多