ASP.Net 核心 3.0 合并角色

ASP.Net Core 3.0 merged roles

在我当前的 Web 应用程序中,我使用声明 "customRole"(加上它的值)对用户进行身份验证,并使用属性 "Authorize" 来 allow/deny 访问操作。 一切正常,直到用户多次声明同一类型,访问一直被拒绝,在调试时我注意到只为类型 "customRole" 创建了一个声明,并且其值更改为串联具有两个值的字符串。

我期待两个相同类型的声明,但每个都有不同的值。 我将 ASPNET.Core 3.0 与 IdentityServer4 一起使用,但根据所写 here,IdentityServer4 不是问题所在。

例如,我在 IdentityServer 端添加这样的声明:

uClaims.Add(new Claim("customRole", "superadmin"));
uClaims.Add(new Claim("customRole", "simpleadmin"));

但是当我进入客户端应用程序时,我得到了这样的声明:

customRole = ["superadmin","simpleadmin"]

这打破了我在客户端应用程序的操作中使用的角色属性背后的所有逻辑:

[Authorize(Roles = "superadmin")]

我尝试按照之前link(here)中讨论的内容进行操作,但问题仍然存在。

是否遗漏了任何内容,导致声明被分开而不是一个与不同的值合并?或者以允许值数组的不同方式使用授权?

还应该提到,我在上个月第一次开始使用 Asp.NetCore、IdentityServer4 和 Roles,我正处于学习曲线中。

谢谢你的时间,干杯

您可以在身份验证后更改您的委托人,并将该声明转换为多个。

        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
        {
          ...
            options.Events = new JwtBearerEvents();
            options.EventsType = typeof(CustomJwtBearerEvents);
        });

        public class CustomJwtBearerEvents : JwtBearerEvents
        {
           public override async Task TokenValidated(TokenValidatedContext context)
           {
              var claims = context.Principal.Claims.ToList();
//write your code
                  claims.Add(new Claim("key", "value"));
                  context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, 
    "Bearer"));        }
        }

找到您的具体声明 ('customclaim'),并检查其价值,如果包含多个价值,则为每个价值添加一个声明。

这是因为 [Authorize(Roles="superadmin")] 属性检查当前身份是否包含 ClaimsIdentity.RoleClaimType(http://schemas.microsoft.com/ws/2008/06/identity/claims/role) 类型的声明,其中声明的值等于 Roles 参数.

superadminsimpleadmincustomRole 的类型,而不是默认的 http://schemas.microsoft.com/ws/2008/06/identity/claims/role 。所以授权失败。您可以配置 AddOpenIdConnect 中间件来设置 RoleClaimType :

JwtSecurityTokenHandler.DefaultMapInboundClaims = false;

services.AddAuthentication(options =>
{
    options.DefaultScheme = "Cookies";
    options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
    options.Authority = "http://localhost:5000";
    options.RequireHttpsMetadata = false;

    options.ClientId = "mvc";
    options.ClientSecret = "secret";
    options.ResponseType = "code";

    options.SaveTokens = true;

    options.Scope.Add("api1");
    options.Scope.Add("offline_access");
    options.TokenValidationParameters.RoleClaimType = "customRole";   //add this line

});

这样 MVC 将在触发 [Authorize(Roles="superadmin")] 时检查 customRole 声明。

我找到原因了。 我在 "Startup"

上使用

.AddOpenIdConnect(IdentityServerConstants.ProtocolTypes.OpenIdConnect, options => { options.ClaimActions.MapUniqueJsonKey("customRole", "customRole"); }

而不是

.AddOpenIdConnect(IdentityServerConstants.ProtocolTypes.OpenIdConnect, options => { options.ClaimActions.MapJsonKey("customRole", "customRole"); }

现在,同一类型的所有声明都被分开,而不是在 clientApp 中连接为一个。我认为我使用了错误的映射器,因为我是从 identityServer4 快速入门教程之一复制的(或谷歌搜索的其他人示例):) 希望,我自己的回答可以防止其他人不得不浪费时间来理解他们应该做的事情:)

@Nan Yu: The correction about the RoleClaimType options solves the problem of using the type = "customRole" instead of the default, but multiple claims of the same type still get concatenated. But, from your comment i learned something new, customizing the roleClaimType, thank you for that

@Mehrdad With the configuration i'm using, the claim is not listed on the context, only claims like "nbf", "iss", "aud" etc, show up, custom ones dont show up here (dunno why). But still, i messed around and found out that it is possible, like you said, to add extra claims here, so it could be useful in future, thanks for sharing the info