在Identity Server 4的id_token中包含核心身份角色声明

11

在使用Identity Server 4(IS4)与AspNet Core Identity集成的成功登录后,我遇到了包含角色声明的问题。这阻止我使用“Authorize(Roles=xxx)”属性来保护对API的访问。

我按照Identity Server 4 / AspNet Identity集成文档中提供的样例进行操作。令人惊讶的是,文档中没有包含角色声明的示例,我认为这是一个非常普遍的场景。

我根据IS4文档设置了3个项目,其中Host指定了HybridClientCredential授权类型,创建了AspNet Identity DB,并手动将角色(“Admin”)添加到EF Core生成的数据库中。我希望在成功登录后自动将角色包含在用户声明中。

  • AspNet Core(个人身份验证)/ Identity Server(主机)
  • MVC应用程序(客户端)
  • MVC Web API(API)

这是我正在使用的代码:

主机:

public class Config
{
    public static IEnumerable<ApiResource> GetApiResources()
    {
        return new[]
        {
            // expanded version if more control is needed
            new ApiResource
            {
                Name = "api1",

                Description = "My API",

                // secret for using introspection endpoint
                ApiSecrets =
                {
                    new Secret("secret".Sha256())
                },

                // include the following using claims in access token (in addition to subject id)
                UserClaims = { "role" },

                // this API defines two scopes
                Scopes =
                {
                    new Scope()
                    {
                        Name = "api1",
                        DisplayName = "Full access to API 1",
                        UserClaims = new [] { "role" }
                    }
                }
            }
        };
    }

    public static IEnumerable<IdentityResource> GetIdentityResources()
    {
        return new List<IdentityResource>
        {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile()
        };
    }

    public static IEnumerable<Client> GetClients()
    {
        return new List<Client>()
        {
            new Client
            {
                ClientId = "mvc",
                ClientName = "MVC Client",
                AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,

                RequireConsent = false,

                ClientSecrets =
                {
                    new Secret("secret".Sha256())
                },

                RedirectUris           = { "http://localhost:5002/signin-oidc" },
                PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
                AlwaysIncludeUserClaimsInIdToken = true,
                AllowedScopes =
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    "api1"
                },
                AllowOfflineAccess = true
            }
        };
    }

}

客户:

public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();
        Configuration = builder.Build();
    }

    public IConfigurationRoot Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        services.AddMvc();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseStaticFiles();

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationScheme = "Cookies"
        });

        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

        app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
        {
            AuthenticationScheme = "oidc",
            SignInScheme = "Cookies",

            Authority = "http://localhost:5000",
            RequireHttpsMetadata = false,

            ClientId = "mvc",
            ClientSecret = "secret",

            ResponseType = "code id_token",
            Scope = { "api1", "offline_access" },

            GetClaimsFromUserInfoEndpoint = true,
            SaveTokens = true,

            TokenValidationParameters = new TokenValidationParameters
            {
                NameClaimType = JwtClaimTypes.Name,
                RoleClaimType = JwtClaimTypes.Role,
            }
        });

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

Api:

[Route("api/[controller]")]
[Authorize(Roles="Admin")]
public class ValuesController : Controller
{
    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }

    // GET api/values/5
    [HttpGet("{id}")]
    public string Get(int id)
    {
        return "value";
    }

    // POST api/values
    [HttpPost]
    public void Post([FromBody]string value)
    {
    }

    // PUT api/values/5
    [HttpPut("{id}")]
    public void Put(int id, [FromBody]string value)
    {
    }

    // DELETE api/values/5
    [HttpDelete("{id}")]
    public void Delete(int id)
    {
    }
}

我已经让设置运作起来了,但是主机在成功登录后没有包含角色声明。 我想知道是否有人可以帮助我解决这个问题? 谢谢。

我已经让设置运作起来了,但是主机在成功登录后没有包含角色声明。我想知道是否有人可以帮助我解决这个问题?谢谢。


你修复了这个问题吗?我也遇到了。 - Alexandra Damaschin
1个回答

11

你好,这是我创建自定义策略角色的方法:

1)

在 Config -> Client 部分添加:

 Claims = new Claim[]
 {
     new Claim("Role", "admin")
 }

2)然后在 Api -> Startup.cs -> ConfigureServices 中添加:

    services.AddAuthorization(options =>
    {
        options.AddPolicy("admin", policyAdmin =>
        {
            policyAdmin.RequireClaim("client_Role", "Admin");
        });

        //otherwise you already have "api1" as scope

        options.AddPolicy("admin", builder =>
        {
            builder.RequireScope("api1");
        });
    });

3)

然后按照以下方式使用:

    [Route("api/[controller]")]
    [Authorize("admin")]
    public class ValuesController : Controller

如果您分析令牌,您将得到以下内容:

{
  "alg": "RS256",
  "kid": "2f2fcd9bc8c2e54a1f29acf77b2f1d32",
  "typ": "JWT"
}

{
  "nbf": 1513935820,
  "exp": 1513937620,
  "iss": "http://localhost/identityserver",
  "aud": [
    "http://localhost/identityserver/resources",
    "MySecuredApi"
  ],
  "client_id": "adminClient",
  "client_Role": "admin",       <---------------
  "scope": [
    "api.full_access",
    "api.read_only"
  ]
}

PS:

由于使用Identity Server 4,您无法使用“RequireRole”功能。当您使用以下代码时:

Claims = new Claim[]
{
    new Claim("Role", "admin")
},

将创建:

client_Role: admin

但是"RequireRole"使用:

Role: admin

因此,它不会匹配。

你可以进行测试:

using System.Security.Claims;
MessageBox.Show("" + new Claim("Role", "admin"));

使用 RequireRole 更新:清除“ClientClaimsPrefix”

在 Config -> Client 部分添加:

ClientClaimsPrefix = "",
Claims = new Claim[]
{
 new Claim(ClaimTypes.Role, "Admin")
}

然后在 Api -> Startup.cs -> ConfigureServices 中添加:

services.AddAuthorization(options =>
{
    options.AddPolicy("admin", builder =>
    {
        builder.RequireRole(new[] { "Admin" });
    });
});

然后这样使用:

    [Route("api/[controller]")]
    [Authorize("admin")]
    public class ValuesController : Controller
否则,如果没有"Policy",使用如下方式:
[Authorize(Roles = "Admin")]

这个完全有效,谢谢。此外,我看到类似的问题已经在这里记录,并提到了有关ClientClaimsPrefix设置的文档 - JvR

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