我已经配置了 Keycloak 和公共客户端,它们只是响应前端应用程序和 web-api 后端。身份验证工作正常。用户打开首页,重定向到 Keycloak 登录\密码表单,获取他的 access_token 并使用 header 中的 access_token 对 web-api 进行一些请求。看起来不错,按预期工作。
现在我想做更复杂的案例。我的应用程序需要授权用户使用特定对象,如下所示。例如,我有 URL /api/v1/school/123 的对象,并且希望 User1 能够获取它,但不是 User2。并且这种“学校”对象的每个所有者都决定他想为谁授予权限。看起来像 ACL,对吧?我发现像 UMA 授权和 Keycloak 这样的过程可以做到这一点(至少乍一看)。
现在变得有趣了。
此外,我创建了 keycloak 机密客户端,为其启用了授权、UMA 和令牌交换功能。根据文档https://www.keycloak.org/docs/latest/authorization_services/#_service_overview我需要将请求发送到 /token 端点并在结尾。首先,我需要获得 PAT 令牌:
// Getting PAT
TokenResponse tokenResponse = await _tokenService.RequestToken(new TokenRequest()
{
GrantType = GrantTypes.ClientCredentials,
ClientId = _options.ClientId,
ClientSecret = _options.ClientSecret
}, null);
var PATAuthzHeader = new AuthenticationHeaderValue("Bearer", tokenResponse.AccessToken);
获得 PAT 后,我能够通过请求 URL“找到”真实资源 ID:
// We have to find resource by it's uri, because user or client doesn't know anything about resources. They have request path only.
var resources = await _resourceService.FindResourceAsync(null, null, requestPath,
null, null, null, null, null, PATAuthzHeader);
if (resources.Count == 0)
{
return false;
}
有了资源 ID,我们终于可以请求了:
var currentAuthorizationHeader = AuthenticationHeaderValue.Parse(context.HttpContext.Request.Headers["Authorization"]);
// With resource id we can check permissions by sending RPT request to token endpoint
// We also send request's method (get, post and so on) to separate CRUD operations
var decisionResponse = await _tokenService.GetRPTDecision(new TokenRequest()
{
GrantType = GrantTypes.UmaTicket,
ResponseMode = ResponseMode.Decision,
Audience = _options.ClientId,
Permission = $"{resourceId}#{context.HttpContext.Request.Method.ToLower()}"
}, currentAuthorizationHeader);
// If the authorization request does not map to any permission, a 403 HTTP status code is returned instead.
最后我们做到了!伟大的!现在一切都开始了……
您可能会注意到,在我们检查权限之前,我们必须通过它的 URL 找到资源 ID。棘手的部分是我们可以使用通配符 URI 创建(并且可能会这样做)资源,例如 /api/v1/schools/123/*,以授予所有者对编号为“123”的学校的每个子对象的全面权限。这意味着当所有者向 /api/v1/schools/123/classes/3/students 发送请求时,他应该被允许这样做并得到响应。你会问:“你为什么不提前用那个特定的 URI 创建资源呢?”。我试过了,但在创建编号为“123”的学校时我不能这样做。毕竟 Keycloak 告诉我们它支持 URI 中的通配符。但请求https://www.keycloak.org/docs/latest/authorization_services/#querying-resources具有此类 url '/api/v1/schools/123/classes/3/students' 的端点返回空数组。它找不到我的资源。
那时我明白 Keycloak 并没有我想象的那么好。我试图找到任何对我有帮助的文档,但他们没有。我得到的一切都是在 github 上的回答:“看看我们的资源。”。伟大的。挖掘java源代码(只是提醒我使用asp net core)不是我要找的。我别无选择,只能自己实施 Policy Enforcer。
正如墨菲定律所说:任何可能出错的事情都会出错。
现在越来越不高兴了。
那么我的执法者形象是什么?它只是一个神奇的盒子,可以从 Keycloak Server 加载每个资源、策略和权限,并像应用程序和 Keycloak Server 之间的代理一样工作,评估内部请求,并且不时与 Keycloak Server 同步以获取或发送更改。所以,让我们这样做吧!但是等等,我怎样才能从 Keycloak 的客户端(或者确切地说是资源服务器)获取所有资源?从https://www.keycloak.org/docs/latest/authorization_services/#querying-resources查询它们 只会返回如下一组 guid:
[
"f8cc15ad-b2e5-46f3-88c6-e0e7cd2ffe4d",
"540aafb9-3c3a-4a99-a6d2-c089c607d5ec",
"9bdf0702-4ee3-421e-9ac8-6ea1b699e073",
"838a6194-3153-473e-9b0b-4f395f49d5cb"
]
但我需要资源的 URI!把它给我!
现在我在这里。询问您是否有其他方法可以获得它,但不为每个给定的 guid 发送单独的请求?如果您知道使用 keycloak REST API 授权用户的更短方法,那会更好吗?