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

时间:2017-08-26 22:28:07

标签: asp.net-identity identityserver4

我遇到了一个问题,包括成功登录Identity Server 4(IS4)并与AspNet Core Identity集成后的角色声明。这使我无法使用"授权(角色= xxx)"属性以保护对api的访问。

我正在关注Identity Server 4 / AspNet Identity集成文档中提供的示例。令人惊讶的是,文档没有一个示例来包含角色声明,我认为这是一种非常常见的情况。

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

  • AspNet Core(个人身份验证)/身份服务器(主机)
  • MVC App(客户端)
  • 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)
    {
    }
}

我的设置正常工作,但成功登录后主机不包括角色声明。

我想知道是否有人可以帮助让我知道如何解决这个问题?感谢。

1 个答案:

答案 0 :(得分:3)

嗨,这是我创建基于角色的自定义策略的方式,

1)

在Config中 - &gt;客户部分添加:

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

2)

然后在Api - &gt; startup.cs - &gt; ConfigureServices add:

    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:

你不能使用&#34; RequireRole&#34;因为Identity Server 4 当你使用:

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

将创建:

client_Role: admin

但是&#34; RequireRole&#34;用途:

Role: admin

所以它不会匹配。

您可以测试:

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

使用RequireRole进行更新:清除&#34; ClientClaimsPrefix&#34;

在Config中 - &gt;客户部分添加:

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

然后在Api - &gt; startup.cs - &gt; ConfigureServices add:

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

然后以这种方式使用它:

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

否则没有&#34;政策&#34; 像这样使用:

[Authorize(Roles = "Admin")]