将 ASP.NET 角色授权与 IdentityServer3 隐式流程结合使用

Using ASP.NET Role Authorisation with IdentityServer3 implicit flow

我的单页应用程序使用 OidcTokenManager 通过隐式流连接到 IdentityServer3 STS。客户端将 IDS3 访问令牌作为承载令牌提供给 ASP.NET 核心 (WebApi) Web 服务; Web 服务应用程序配置为使用 IDS3 中间件并使用 Authorize 属性限制对其方法的访问。

SPA客户端配置:

function configureTokenManager() {
    console.log("configureTokenManager()");
    var config = {
        authority: $config.authority,
        client_id: "BNRegistry",
        redirect_uri: $config.webRoot + "/#/authorised/",  
        post_logout_redirect_uri: $config.webRoot + "/#/",

        response_type: "id_token token",
        scope: "openid profile email BNApi",

        silent_redirect_uri: $config.webRoot + "/#/renew/",
        silent_renew: true,

        filter_protocol_claims: false
    };
    return new OidcTokenManager(config);
};

STS 中的作用域配置:

new Scope
    {
        Name = "BNApi",
        DisplayName = "BN Api",
        Enabled = true,
        Type = ScopeType.Resource,
        Claims = new List<ScopeClaim>
        {
            new ScopeClaim(Constants.ClaimTypes.Name),
            new ScopeClaim(Constants.ClaimTypes.Role)
        }
    }

WebApi 配置:

app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
    {
        Authority = Configuration["Authority"],
        RequiredScopes = new[] {"BNApi"},
        NameClaimType = IdentityModel.JwtClaimTypes.Name,                
        RoleClaimType = IdentityModel.JwtClaimTypes.Role
    });

WebApi 方法:

[Authorize]
public IActionResult Get()
{
    ...
}

这按预期工作,使用 401 拒绝未经身份验证的用户。如果我在 api 控制器方法(例如 User.Claims.ToList())中检查用户的声明,它包含以下条目已分配给用户的任何角色。

但是,如果我检查 User.Identity.Name 属性 它总是空的,如果我查询 User.IsInRole("Administrator") 它总是假的,即使用户被分配到那个角色。此外,如果我将角色名称添加到 Authorize 属性 ([Authorize(Role="Administrator")]),无论用户是否属于指定角色,都会被 401 拒绝。

如何让IdentityServer3与ASP.NET角色授权一起玩?

您是否尝试过重置 InboundClaimTypeMap

来自 IdentityServer3 文档页面 here:

When you inspect the claims on the about page, you will notice two things: some claims have odd long type names and there are more claims than you probably need in your application.

The long claim names come from Microsoft’s JWT handler trying to map some claim types to .NET's ClaimTypes class types.

不幸的是,此映射最终破坏了您定义为 namerole 的特定声明名称,因为它们的名称已转换并且不再映射到您所期望的名称。这导致 [Authorize(Roles = "")]User.IsInRole("") 无法按预期工作。

在您的 API Startup.cs 中,您应该添加以下内容:

JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();

app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions()
{
    ...
});

编辑: 以下信息不正确!。正如@Paul Taylor "the AlwaysInclude property ensures that the relevant claim is always present in an identity token (which is used with the client, not the API). This is a resource scope so the property has no effect." 指出的那样。感谢您帮助我更多地了解 IdentityServer 的工作原理:-)

对于访问 API 时要包含的 NameRole 声明,您需要在 ScopeClaim 列表中将它们明确标记为 alwaysInclude

new Scope
    {
        Name = "BNApi",
        DisplayName = "BN Api",
        Enabled = true,
        Type = ScopeType.Resource,
        Claims = new List<ScopeClaim>
        {
            new ScopeClaim(Constants.ClaimTypes.Name, true), //<-- Add true here
            new ScopeClaim(Constants.ClaimTypes.Role, true) // and here!
        }
    }