使用 Keycloak 和 .NET 核心的基于角色的授权
Role based authorization using Keycloak and .NET core
dotnet core 2.2.3 和 Keycloak 4.5.0 基于角色的授权存在一些小问题。
在 Keycloak 中,我定义了 'tester' 角色和 'developer' 客户端角色,并为 'admin' 用户定义了适当的角色映射。向 Keycloak 进行身份验证后;如果我查看 jwt.io 中的 JWT,我可以看到以下内容:
{
"realm_access": {
"roles": [
"tester"
]
},
"resource_access": {
"template": {
"roles": [
"developer"
]
},
...
},
...
}
在 .NET 核心中,我尝试了很多方法,例如将 [Authorize(Roles = "tester")]
或 [Authorize(Roles = "developer")]
添加到我的控制器方法中,以及在我检查 [=14 时使用基于策略的授权=] 在我的 AuthorizationHandler<TRequirement>
实现中。
如果我在授权处理程序中设置一些断点。当它被击中时,我可以看到 'tester' 和 'developer' 角色列为 context.user.Claims
IEnumerable 下的项目,如下所示。
{realm_access: {"roles":["tester"]}}
{resource_access: {"template":{"roles":["developer"]}}}
所以我应该能够通过验证[=16]中realm_access
和resource_access
的值在auth处理程序中成功进行授权=] 集合,但这需要我反序列化声明值,它似乎只是 JSON 字符串。
我在想必须有更好的方法,否则我做的事情不是很正确。
"AspNetCore.Authorization" 期望角色在名为 "roles" 的声明(字段)中。并且这个声明必须是一个 字符串数组 (多值)。您需要在 Keycloak 端进行一些配置。
第一个选择:
您可以更改现有的角色路径。
转到您的 Keycloak 管理控制台 > 客户端范围 > 角色 > 映射器 > 客户端角色
- 将 "Token Claim Name" 更改为 "roles"
- 多值:正确
- 添加到访问令牌:True
第二个选择:
如果您不想触及现有路径,您可以创建一个新的 Mapper 以在根目录中显示相同的角色。
转到您的 Keycloak 管理控制台 > 客户端范围 > 角色 > 映射器 > 创建
- 姓名:"root client roles"(或任何你想要的)
- 映射器类型:"User Client Role"
- 多值:正确
- 令牌声明名称:"roles"
- 添加到访问令牌:True
第 4 种选择:在票据接收事件中从 JWT 读取角色。这是代码片段:
options.Events.OnTicketReceived = ctx =>
{
List<AuthenticationToken> tokens = ctx.Properties!.GetTokens().ToList();
ClaimsIdentity claimsIdentity = (ClaimsIdentity) ctx.Principal!.Identity!;
foreach (AuthenticationToken t in tokens)
{
claimsIdentity.AddClaim(new Claim(t.Name, t.Value));
}
var access_token = claimsIdentity.FindFirst((claim) => claim.Type == "access_token")?.Value;
var handler = new JwtSecurityTokenHandler();
var jwtSecurityToken = handler.ReadJwtToken(access_token);
JObject obj = JObject.Parse(jwtSecurityToken.Claims.First(c => c.Type == "resource_access").Value);
var roleAccess = obj.GetValue("your_client_id")!.ToObject<JObject>()!.GetValue("roles");
foreach (JToken role in roleAccess!)
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, role.ToString()));
}
return Task.CompletedTask;
};
dotnet core 2.2.3 和 Keycloak 4.5.0 基于角色的授权存在一些小问题。
在 Keycloak 中,我定义了 'tester' 角色和 'developer' 客户端角色,并为 'admin' 用户定义了适当的角色映射。向 Keycloak 进行身份验证后;如果我查看 jwt.io 中的 JWT,我可以看到以下内容:
{
"realm_access": {
"roles": [
"tester"
]
},
"resource_access": {
"template": {
"roles": [
"developer"
]
},
...
},
...
}
在 .NET 核心中,我尝试了很多方法,例如将 [Authorize(Roles = "tester")]
或 [Authorize(Roles = "developer")]
添加到我的控制器方法中,以及在我检查 [=14 时使用基于策略的授权=] 在我的 AuthorizationHandler<TRequirement>
实现中。
如果我在授权处理程序中设置一些断点。当它被击中时,我可以看到 'tester' 和 'developer' 角色列为 context.user.Claims
IEnumerable 下的项目,如下所示。
{realm_access: {"roles":["tester"]}}
{resource_access: {"template":{"roles":["developer"]}}}
所以我应该能够通过验证[=16]中realm_access
和resource_access
的值在auth处理程序中成功进行授权=] 集合,但这需要我反序列化声明值,它似乎只是 JSON 字符串。
我在想必须有更好的方法,否则我做的事情不是很正确。
"AspNetCore.Authorization" 期望角色在名为 "roles" 的声明(字段)中。并且这个声明必须是一个 字符串数组 (多值)。您需要在 Keycloak 端进行一些配置。
第一个选择:
您可以更改现有的角色路径。
转到您的 Keycloak 管理控制台 > 客户端范围 > 角色 > 映射器 > 客户端角色
- 将 "Token Claim Name" 更改为 "roles"
- 多值:正确
- 添加到访问令牌:True
第二个选择:
如果您不想触及现有路径,您可以创建一个新的 Mapper 以在根目录中显示相同的角色。
转到您的 Keycloak 管理控制台 > 客户端范围 > 角色 > 映射器 > 创建
- 姓名:"root client roles"(或任何你想要的)
- 映射器类型:"User Client Role"
- 多值:正确
- 令牌声明名称:"roles"
- 添加到访问令牌:True
第 4 种选择:在票据接收事件中从 JWT 读取角色。这是代码片段:
options.Events.OnTicketReceived = ctx =>
{
List<AuthenticationToken> tokens = ctx.Properties!.GetTokens().ToList();
ClaimsIdentity claimsIdentity = (ClaimsIdentity) ctx.Principal!.Identity!;
foreach (AuthenticationToken t in tokens)
{
claimsIdentity.AddClaim(new Claim(t.Name, t.Value));
}
var access_token = claimsIdentity.FindFirst((claim) => claim.Type == "access_token")?.Value;
var handler = new JwtSecurityTokenHandler();
var jwtSecurityToken = handler.ReadJwtToken(access_token);
JObject obj = JObject.Parse(jwtSecurityToken.Claims.First(c => c.Type == "resource_access").Value);
var roleAccess = obj.GetValue("your_client_id")!.ToObject<JObject>()!.GetValue("roles");
foreach (JToken role in roleAccess!)
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, role.ToString()));
}
return Task.CompletedTask;
};