繁体   English   中英

身份服务器 4:向访问令牌添加声明

[英]Identity Server 4: adding claims to access token

我正在使用 Identity Server 4 和隐式流,并想向访问令牌添加一些声明,新声明或属性是“tenantId”和“langId”。

我已将 langId 添加为我的范围之一,如下所示,然后通过身份服务器请求它,但我也得到了租户 ID。 这怎么会发生?

这是范围和客户端配置的列表:

  public IEnumerable<Scope> GetScopes()
    {
        return new List<Scope>
        {
             // standard OpenID Connect scopes
            StandardScopes.OpenId,
            StandardScopes.ProfileAlwaysInclude,
            StandardScopes.EmailAlwaysInclude,

            new Scope
            {
                Name="langId",
                 Description = "Language",
                Type= ScopeType.Resource,
                Claims = new List<ScopeClaim>()
                {
                    new ScopeClaim("langId", true)
                }
            },
            new Scope
            {
                Name = "resourceAPIs",
                Description = "Resource APIs",
                Type= ScopeType.Resource
            },
            new Scope
            {
                Name = "security_api",
                Description = "Security APIs",
                Type= ScopeType.Resource
            },
        };
    }

客户:

  return new List<Client>
        {
            new Client
            {
                ClientName = "angular2client",
                ClientId = "angular2client",
                AccessTokenType = AccessTokenType.Jwt,
                AllowedGrantTypes = GrantTypes.Implicit,
                AllowAccessTokensViaBrowser = true,
                RedirectUris = new List<string>(redirectUris.Split(',')), 
                PostLogoutRedirectUris = new List<string>(postLogoutRedirectUris.Split(',')),
                AllowedCorsOrigins = new List<string>(allowedCorsOrigins.Split(',')),

                AllowedScopes = new List<string>
                {
                   "openid",
                   "resourceAPIs",
                   "security_api",         
                   "role",
                  "langId"
                }
            }
        };

我在ProfileService 中添加了声明:

 public class ProfileService : IdentityServer4.Services.IProfileService
{
    private readonly SecurityCore.ServiceContracts.IUserService _userService;


    public ProfileService(SecurityCore.ServiceContracts.IUserService userService)
    {
        _userService = userService;
    }

    public Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
       //hardcoded them just for testing purposes
        List<Claim> claims = new List<Claim>() { new Claim("langId", "en"), new Claim("tenantId", "123") };

        context.IssuedClaims = claims;


        return Task.FromResult(0);
    }

这就是我请求获得令牌,问题是我只请求LANGID但我在访问令牌获得两个tenantIdLANGID

http://localhost:44312/account/login?returnUrl=%2Fconnect%2Fauthorize%2Flogin%3Fresponse_type%3Did_token%2520token%26client_id%3Dangular2client%26redirect_uri%3Dhttp%253A%252F%252Flocalhost:5002%26scope%3DresourceAPIs%2520notifications_api%2520security_api%2520langId%2520navigation_api%2520openid%26nonce%3DN0.73617935552798141482424408851%26state%3D14824244088510.41368537145696305%26

解码访问令牌:

 {
  "nbf": 1483043742,
  "exp": 1483047342,
  "iss": "http://localhost:44312",
  "aud": "http://localhost:44312/resources",
  "client_id": "angular2client",
  "sub": "1",
  "auth_time": 1483043588,
  "idp": "local",
  "langId": "en",
  "tenantId": "123",
  "scope": [
    "resourceAPIs",     
    "security_api",
    "langId",
    "openid"
  ],
  "amr": [
    "pwd"
  ]
}

这个答案是为 .Net core 2 上的 Identityserver4 编写的,用于 .Net core 3,这个答案可能对你有帮助,但你需要测试和更改一些东西。


我正在使用带有 Identityserver4 的 asp.net 身份和实体框架。

这是我的示例代码,运行良好,JWT 包含所有角色和声明

您可以在此处查看如何使用 ASP.Net 核心身份实现 Identityserver4 http://docs.identityserver.io/en/release/quickstarts/6_aspnet_identity.html https://github.com/IdentityServer/IdentityServer4.Samples/tree/dev /快速入门/6_AspNetIdentity

1-身份服务器startup.cs

using IdentityServer4;
    using IdentityServer4.Models;
    using System.Collections.Generic;

    namespace IdentityAuthority.Configs
    {

        public class IdentityServerConfig
        {

            // scopes define the resources in your system
            public static IEnumerable<IdentityResource> GetIdentityResources()
            {
                return new List<IdentityResource>
                {
                    new IdentityResources.OpenId(),
                    new IdentityResources.Profile()
                };
            }

            // scopes define the API resources
            public static IEnumerable<ApiResource> GetApiResources()
            {
                //Create api resource list
                List<ApiResource> apiResources = new List<ApiResource>();

                //Add Application Api API resource
                ApiResource applicationApi = new ApiResource("ApplicationApi", "Application Api");
                applicationApi.Description = "Application Api resource.";
                apiResources.Add(applicationApi);

                //Add Application Api API resource
                ApiResource definitionApi = new ApiResource("DefinitionApi", "Definition Api");
                definitionApi.Description = "Definition Api.";
                apiResources.Add(definitionApi);

                //Add FF API resource
                ApiResource ffApi = new ApiResource("FFAPI", "Fule .netfx API");
                ffApi.Description = "Test using .net 4.5 API application with IdentityServer3.AccessTokenValidation";
                apiResources.Add(ffApi);

                return apiResources;
            }

            // client want to access resources (aka scopes)
            public static IEnumerable<Client> GetClients()
            {
                //Create clients list like webui, console applications and...
                List<Client> clients = new List<Client>();

                //Add WebUI client
                Client webUi = new Client();
                webUi.ClientId = "U2EQlBHfcbuxUo";
                webUi.ClientSecrets.Add(new Secret("TbXuRy7SSF5wzH".Sha256()));
                webUi.ClientName = "WebUI";
                webUi.AllowedGrantTypes = GrantTypes.HybridAndClientCredentials;
                webUi.RequireConsent = false;
                webUi.AllowOfflineAccess = true;
                webUi.AlwaysSendClientClaims = true;
                webUi.AlwaysIncludeUserClaimsInIdToken = true;
                webUi.AllowedScopes.Add(IdentityServerConstants.StandardScopes.OpenId);
                webUi.AllowedScopes.Add(IdentityServerConstants.StandardScopes.Profile);
                webUi.AllowedScopes.Add("ApplicationApi");
                webUi.AllowedScopes.Add("DefinitionApi");
                webUi.AllowedScopes.Add("FFAPI");
                webUi.ClientUri = "http://localhost:5003";
                webUi.RedirectUris.Add("http://localhost:5003/signin-oidc");
                webUi.PostLogoutRedirectUris.Add("http://localhost:5003/signout-callback-oidc");
                clients.Add(webUi);

                //Add IIS test client
                Client iisClient = new Client();
                iisClient.ClientId = "b8zIsVfAl5hqZ3";
                iisClient.ClientSecrets.Add(new Secret("J0MchGJC8RzY7J".Sha256()));
                iisClient.ClientName = "IisClient";
                iisClient.AllowedGrantTypes = GrantTypes.HybridAndClientCredentials;
                iisClient.RequireConsent = false;
                iisClient.AllowOfflineAccess = true;
                iisClient.AlwaysSendClientClaims = true;
                iisClient.AlwaysIncludeUserClaimsInIdToken = true;
                iisClient.AllowedScopes.Add(IdentityServerConstants.StandardScopes.OpenId);
                iisClient.AllowedScopes.Add(IdentityServerConstants.StandardScopes.Profile);
                iisClient.AllowedScopes.Add("ApplicationApi");
                iisClient.AllowedScopes.Add("DefinitionApi");
                iisClient.AllowedScopes.Add("FFAPI");
                iisClient.ClientUri = "http://localhost:8080";
                iisClient.RedirectUris.Add("http://localhost:8080/signin-oidc");
                iisClient.PostLogoutRedirectUris.Add("http://localhost:8080/signout-callback-oidc");
                clients.Add(iisClient);

                return clients;
            }

        }
    }

2- IdentityServerConfig.cs

using IdentityServer4.Services;
using System;
using System.Threading.Tasks;
using IdentityServer4.Models;
using IdentityAuthority.Models;
using Microsoft.AspNetCore.Identity;
using IdentityServer4.Extensions;
using System.Linq;

namespace IdentityAuthority.Configs
{
    public class IdentityProfileService : IProfileService
    {

        private readonly IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory;
        private readonly UserManager<ApplicationUser> _userManager;

        public IdentityProfileService(IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory, UserManager<ApplicationUser> userManager)
        {
            _claimsFactory = claimsFactory;
            _userManager = userManager;
        }

        public async Task GetProfileDataAsync(ProfileDataRequestContext context)
        {
            var sub = context.Subject.GetSubjectId();
            var user = await _userManager.FindByIdAsync(sub);
            if (user == null)
            {
                throw new ArgumentException("");
            }

            var principal = await _claimsFactory.CreateAsync(user);
            var claims = principal.Claims.ToList();

            //Add more claims like this
            //claims.Add(new System.Security.Claims.Claim("MyProfileID", user.Id));

            context.IssuedClaims = claims;
        }

        public async Task IsActiveAsync(IsActiveContext context)
        {
            var sub = context.Subject.GetSubjectId();
            var user = await _userManager.FindByIdAsync(sub);
            context.IsActive = user != null;
        }
    }

}

3 - IdentityProfileService.cs

 using IdentityServer4.Services; using System; using System.Threading.Tasks; using IdentityServer4.Models; using IdentityAuthority.Models; using Microsoft.AspNetCore.Identity; using IdentityServer4.Extensions; using System.Linq; namespace IdentityAuthority.Configs { public class IdentityProfileService : IProfileService { private readonly IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory; private readonly UserManager<ApplicationUser> _userManager; public IdentityProfileService(IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory, UserManager<ApplicationUser> userManager) { _claimsFactory = claimsFactory; _userManager = userManager; } public async Task GetProfileDataAsync(ProfileDataRequestContext context) { var sub = context.Subject.GetSubjectId(); var user = await _userManager.FindByIdAsync(sub); if (user == null) { throw new ArgumentException(""); } var principal = await _claimsFactory.CreateAsync(user); var claims = principal.Claims.ToList(); //Add more claims like this //claims.Add(new System.Security.Claims.Claim("MyProfileID", user.Id)); context.IssuedClaims = claims; } public async Task IsActiveAsync(IsActiveContext context) { var sub = context.Subject.GetSubjectId(); var user = await _userManager.FindByIdAsync(sub); context.IsActive = user != null; } } }

4 - 在我的客户端 mvc 核心项目中,我添加了 3 个 nuget 包

.Microsoft.AspNetCore.Authentication.Cookies

.Microsoft.AspNetCore.Authentication.OpenIdConnect

.身份模型

5- 这是我的客户端 mvc 核心项目中的 startup.cs

 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); //app.UseBrowserLink(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); //Setup OpenId and Identity server app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationScheme = "Cookies", AutomaticAuthenticate = true }); app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions { Authority = "http://localhost:5000", ClientId = "U2EQlBHfcbuxUo", ClientSecret = "TbXuRy7SSF5wzH", AuthenticationScheme = "oidc", SignInScheme = "Cookies", SaveTokens = true, RequireHttpsMetadata = false, GetClaimsFromUserInfoEndpoint = true, ResponseType = "code id_token", Scope = { "ApplicationApi", "DefinitionApi", "FFAPI", "openid", "profile", "offline_access" }, TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters { NameClaimType = "name", RoleClaimType = "role" } }); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); }

6 - 在我的 API 中,我添加了这个 nuget 包

.IdentityServer4.AccessTokenValidatio

我的startup.cs是这样的

User.IsInRole("Admin")

现在我可以在客户端 Web 应用程序和 API 应用程序中使用 [Authorize(Role="SuperAdmin, Admin")]。

HttpContext.User.Claims 

var q = (from p in HttpContext.User.Claims where p.Type == "role" select p.Value).ToList();

var q2 = (from p in HttpContext.User.Claims where p.Type == "sub" select p.Value).First();

我也有权获得索赔

HttpContext.User.Claims var q = (from p in HttpContext.User.Claims where p.Type == "role" select p.Value).ToList(); var q2 = (from p in HttpContext.User.Claims where p.Type == "sub" select p.Value).First();

您应该检查context.RequestedClaimTypes并过滤掉未请求的声明。

经过一些严谨的研究,我想提供我自己的答案:

在登录过程中,服务器会发出一个带有用户声明的身份验证 cookie。

然后,客户端将在提供来自 cookie 的声明的同时请求访问令牌,并且配置文件服务将使用 cookie 声明来生成访问令牌声明。

接下来,客户端将请求一个 id 令牌,但这次它将使用来自访问令牌的声明。

现在的情况是,身份服务器的默认配置文件服务仅通过使用访问令牌中的声明来填充 id 令牌的声明,而 ASP.Net Identity 的默认配置文件服务确实从数据库中查找所有用户声明店铺。 这是一个混淆点。

对于身份服务器实现,访问令牌中最终会出现哪些声明? 与作为 API 资源的范围相关联的声明,与 id 令牌中的声明相反,后者是与作为身份资源的范围相关联的声明。

概括

没有 ASP.NET 标识:

  1. 登录 - 身份服务器发出带有某些声明的 cookie
  2. 访问令牌查询 - 身份服务器根据请求的 api 范围从 cookie 添加声明
  3. Id 令牌查询 - 身份服务器根据请求的身份范围添加来自访问令牌的声明

使用 ASP.NET 标识:

  1. 登录 - 身份服务器发出带有某些声明的 cookie
  2. 访问令牌查询 - 身份服务器根据请求的 api 范围从 cookie 添加声明
  3. Id 令牌查询 - 身份服务器根据请求的身份范围添加来自访问令牌的声明和用户存储中的声明

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM