Asp.net core 基于多个角色的IdentityServer4基于角色的授权

Asp.net core 基于多个角色的IdentityServer4基于角色的授权,asp.net-core,.net-core,authorization,identityserver4,Asp.net Core,.net Core,Authorization,Identityserver4,我想使用IdentityServer4 hybrid实现多个基于角色的授权,一切都很好,但当我想这样使用时: [Authorize(Roles = "Admin,SalaryUser")] 它不允许我和拒绝访问 在我的场景中,用户有多个角色,如果角色有效,控制器应授予我访问权限,例如,在上面的代码中,控制器应授予这些用户访问权限: 用户具有SalaryUser角色,用户具有admin角色,用户同时具有admin和SalaryUser角色 以下是配置: .AddOpenIdConnect("oi

我想使用IdentityServer4 hybrid实现多个基于角色的授权,一切都很好,但当我想这样使用时:

[Authorize(Roles = "Admin,SalaryUser")]
它不允许我和拒绝访问

在我的场景中,用户有多个角色,如果角色有效,控制器应授予我访问权限,例如,在上面的代码中,控制器应授予这些用户访问权限: 用户具有SalaryUser角色,用户具有admin角色,用户同时具有admin和SalaryUser角色

以下是配置:

.AddOpenIdConnect("oidc", options =>
                    {
                        options.SignInScheme = "Cookies";

                        options.Authority = authority;
                        options.RequireHttpsMetadata = false;
                        options.ClientId = clientId;
                        options.ClientSecret = "secret";
                        options.ResponseType = "code id_token";
                        options.UseTokenLifetime = false;
                        options.SaveTokens = true;
                        options.GetClaimsFromUserInfoEndpoint = true;





                        options.ClaimActions.MapCustomJson("role", jobj =>
                        {
                            IEnumerable<string> values = jobj["http://schemas.microsoft.com/ws/2008/06/identity/claims/role"].Values<string>();
                            StringBuilder sb = new StringBuilder();
                            foreach (string val in values)
                            {
                                sb.Append(val + ",");
                            }
                            return sb.ToString().TrimEnd(',');
                        });

                        options.Scope.Add("api1");
                        options.Scope.Add("offline_access");
                       // options.Scope.Add("roles");

                        options.Events = new OpenIdConnectEvents()
                        {

                            OnUserInformationReceived = async UserInformationReceivedContext =>
                            {
                                // UserInformationReceivedContext.User.Remove("address");

                                if (UserInformationReceivedContext.User.TryGetValue("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", out JToken role))
                                {
                                    var claims = new List<Claim>();
                                    if (role.Type != JTokenType.Array)
                                    {
                                        claims.Add(new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", (string)role));
                                    }
                                    else
                                    {
                                        foreach (var r in role)
                                            claims.Add(new Claim("role", (string)r));
                                    }
                                    var id = UserInformationReceivedContext.Principal.Identity as ClaimsIdentity;
                                    id.AddClaims(claims);
                                }
                            }
                        };

                        options.ClaimActions.MapAll();
                    });
.AddOpenIdConnect(“oidc”,选项=>
{
options.signnscheme=“Cookies”;
选项。权限=权限;
options.RequireHttpsMetadata=false;
options.ClientId=ClientId;
options.ClientSecret=“secret”;
options.ResponseType=“代码id\U令牌”;
options.UseTokenLifetime=false;
options.SaveTokens=true;
options.GetClaimsFromUserInfoEndpoint=true;
options.claims.MapCustomJson(“角色”,jobj=>
{
IEnumerable values=jobj[“http://schemas.microsoft.com/ws/2008/06/identity/claims/role“].Values();
StringBuilder sb=新的StringBuilder();
foreach(值中的字符串val)
{
sb.追加(val+“,”);
}
将某人返回到字符串()TrimEnd(',');
});
选项。范围。添加(“api1”);
options.Scope.Add(“脱机访问”);
//选项。范围。添加(“角色”);
options.Events=new OpenIdConnectEvents()
{
OnUserInformation Received=异步用户信息ReceivedContext=>
{
//UserInformationReceivedContext.User.Remove(“地址”);
if(UserInformationReceivedContext.User.TryGetValue(“http://schemas.microsoft.com/ws/2008/06/identity/claims/role“,超出了JToken角色)
{
var索赔=新列表();
if(role.Type!=JTokenType.Array)
{
索赔。添加(新索赔(“http://schemas.microsoft.com/ws/2008/06/identity/claims/role“,(字符串)角色”);
}
其他的
{
foreach(角色中的var)
添加(新的索赔(“角色”,(字符串)r));
}
var id=用户信息ReceivedContext.Principal.Identity作为索赔实体;
id.AddClaims(索赔);
}
}
};
options.claims.MapAll();
});
您需要将JWT声明类型映射到Identity使用的声明类型

目前的问题是ASP.NET核心的标识系统依赖于
ClaimTypes.Role
常量 ()至 确定用户的角色。但是,索赔人的姓名是: 与OpenID JWT令牌上的角色相对应的是
role
。任何 当您需要合并OIDC标识和ASP.NET标识时,必须进行翻译 像这样的说法

我重写了一点代码,因为您可以在不使用JTokenType的情况下使用声明标识。虽然我没有时间对其进行彻底测试,但如果它不符合您的代码,请在向标识添加声明时将“role”替换为ClaimTypes.role

OpenIdConnectEvents CreateOpenIdConnectEvents()
{
返回新的OpenIdConnectEvents()
{
OnTicketReceived=context=>
{
var identity=context.Principal.identity作为索赔实体;
如果(标识!=null)
{
if(identity.HasClaim(c=>c.Type==“角色”))
{
foreach(identity.Claims.Where(c=>c.Type==“role”)中的var角色)
{
if(!context.Principal.HasClaim(c=>c.Type==ClaimTypes.Role&&c.Value==Role.Value))
{
AddClaim(新声明(ClaimTypes.Role、Role.Value));
}
}
}
}
返回Task.FromResult(0);
}
};
}
这样使用:

[Authorize(Roles = "Admin,SalaryUser")]
options.Events=new CreateOpenIdConnectEvents()

您不需要使用
MapCustomJson
或在您收到的
OnUserInformation
中手动映射索赔

如果jwt令牌中的声明是
角色
而不是
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role
,您可以设置客户端应用程序的角色验证声明:

options.TokenValidationParameters = new TokenValidationParameters
{
    RoleClaimType = "role"
};
另一种方法是在identity server应用程序上向颁发的令牌添加角色声明时,使用
ClaimTypes.role

new Claim(ClaimTypes.Role, "Admin"),
这篇文章可能会解决这个问题。