如何在Keycloak中限制客户端访问只针对一个用户组?

48

我在 awx(ansible tower)网页中有一个 keycloak 客户端。

我只需要来自特定 keycloak 组的用户能够通过此客户端登录。

如何禁止所有其他用户(除了一个特定组)使用此 keycloak 客户端?


您可以从此文档中获取帮助,其中明确说明了如何实现此目标 https://www.keycloak.org/docs/3.3/server_admin/topics/admin-console-permissions/fine-grain.html - Subodh Joshi
1
您可能发布了错误的链接?Admin Console访问控制和权限仅解释了如何为每个用户/组内部设置权限。我的问题是如何允许仅通过此客户端登录关键字中几个用户。 - lukasell
如果您找不到流程执行的“脚本”,请查看此链接:https://dev59.com/Lq_la4cB1Zd3GeqP0e-m - marconfus
这里提供了一个完美运作的解决方案:https://stackoverflow.com/questions/65331061/user-attribute-based-web-service-access-control-by-keycloak - András Gábor Soós
我从 https://dev59.com/yVMI5IYBdhLWcg3wBGqC#68509979 的步骤中成功了。选择“条件-用户组”而不是答案中指定的“条件-用户角色”。 - Codebling
10个回答

22
我找到了一种解决方案,它不需要脚本扩展或任何对流程的更改。
这种解决方案的关键是客户端范围。一个想要授权用户的应用程序需要像电子邮件或uid这样的作用域,对吧?如果只在特定组中的用户通过时才将它们传递给应用程序会怎么样?
以下内容中,我的客户端应用程序名为App1。
解决方案:
1. 进入您的客户端角色(领域 -> 客户端 -> 点击 App1 -> 角色)。 2. 点击“添加角色”->输入名称(例如“access”)->点击“保存”。 3. 进入客户端范围(领域 -> 客户端范围)。 4. 单击所需的客户端应用程序的范围(例如“email”)。 5. 在“作用域”选项卡中通过选择下拉菜单“客户端角色”中的客户端应用程序“App1”来分配客户端角色“access”。 现在,您将无法登录到客户端应用程序App1中,因为没有将角色“access”分配给任何用户或组。你可以试试。
让我们创建一个新的组并将角色和用户分配给它。
6. 创建组(领域 -> 组 -> 点击“新建” -> 输入名称“App1用户”-> 点击保存)。 7. 在组中选择“角色映射”,在“客户端角色”下拉菜单中选择“App1”,并分配角色“access”。 8. 将用户分配给“App1用户”(领域 -> 用户 -> 点击用户 -> 组 -> 选择“App1用户”-> 点击加入)。
完成,选定的用户现在可以登录到App1中。

1
这个很有效。除了向客户端添加另一个角色之外,还可以添加全局角色(nextcloud-access或类似的角色),并在第4步中使用它。 - Karalga
2
有没有可能根据应用程序限制用户?例如,我在domain.com上有一个应用程序,另一个在domain.com/jenkins上。我能否仅限制一组用户访问domain.com,而另一组用户仅限于访问domain.com/jenkins? - Wonderhost
是的,你不能再登录了。哈哈。但在我的Django案例中,用户会得到一个500错误:NOT NULL约束失败:django_keycloak_keycloakuserautoid.email。 - EinEsellesEniE
2
这对我没有用 - 用户仍然可以进行身份验证(Keycloak 19.0.2) - Codebling
1
就像@Codebling提到的那样,这对于我在Keycloak 20.0上也没有起作用。仍然可以接收访问令牌。我唯一注意到的是,所需的“范围”未呈现到访问令牌中,因此一个解决方法可能是您的资源服务器(要保护的应用程序)检查令牌中是否存在某个特定范围。 - Robin Windey
显示剩余2条评论

16
在Keycloak管理控制台中,转到Clients菜单,选择您的客户端。在客户端配置页面上,将Authorization Enabled: On设置为开启状态,然后点击Save。一个新的Authorization选项卡应该出现,进入它,然后进入下面的Policies选项卡,点击Create Policy并选择Group-based policy。在那里,您可以限制对特定组的访问权限,假设您已经通过Groups菜单定义了您的组。

--2019-11-08编辑--

如评论中所述,必须将Client Protocol设置为openid-connect,并且必须将Access Type设置为confidential,才能使Authorization Enabled选项可见。


这在openid中完美地运作。但是在SAML中,没有选择通过按钮启用授权。 - lukasell
确实 :-( 现在我意识到了这个(大)Keycloak在SAML方面的限制。很高兴你找到了一个解决方法。 - cdan
1
对于那些没有“启用授权”的人,请将访问类型设置为“机密”,然后它就会出现。 - Kevin C
23
我按照指示操作了,但是验证没有被任何方式阻拦。对我来说不起作用。 - Réda Housni Alaoui
12
这不会生效,因为Keycloak中的Authorization是指使用Authorization Services API。这需要客户端应用程序显式支持和执行,而Keycloak本身不会强制执行策略。 - F30
这对我没有用,因为客户端是保密的,但是Keycloak JS客户端已经停止支持发送客户端凭据,如果您在登录时不发送凭据,则浏览器会出现CORS错误。 - Asu

8
如果有帮助的话,这里有一个脚本可以帮助任何客户端实现这种行为:如果客户端包含特定的角色(这里称为feature:authenticate),则脚本会检查用户是否具有该角色,并在没有时显示错误页面(需要在主题中部署的新模板)。
AuthenticationFlowError = Java.type("org.keycloak.authentication.AuthenticationFlowError");

 function authenticate(context) {
    var MANDATORY_ROLE = 'feature:authenticate';
    var username = user ? user.username : "anonymous";

    var client = session.getContext().getClient();

    LOG.debug("Checking access to authentication for client '" + client.getName() + "' through mandatory role '" + MANDATORY_ROLE + "' for user '" + username + "'");

    var mandatoryRole = client.getRole(MANDATORY_ROLE);

    if (mandatoryRole === null) {
        LOG.debug("No mandatory role '" + MANDATORY_ROLE + "' for client '" + client.getName() + "'");
        return context.success();
    }

    if (user.hasRole(mandatoryRole)) {
        LOG.info("Successful authentication for user '" + username + "' with mandatory role '" + MANDATORY_ROLE + "' for client '" + client.getName() + "'");
        return context.success();
    }

    LOG.info("Denied authentication for user '" + username + "' without mandatory role '" + MANDATORY_ROLE + "' for client '" + client.getName() + "'");
    return denyAccess(context, mandatoryRole);
 }

 function denyAccess(context, mandatoryRole) {
    var formBuilder = context.form();
    var client = session.getContext().getClient();
    var description = !mandatoryRole.getAttribute('deniedMessage').isEmpty() ? mandatoryRole.getAttribute('deniedMessage') : [''];
    var form = formBuilder
        .setAttribute('clientUrl', client.getRootUrl())
        .setAttribute('clientName', client.getName())
        .setAttribute('description', description[0])
        .createForm('denied-auth.ftl');
    return context.failure(AuthenticationFlowError.INVALID_USER, form);
 }

我们如何实现和使用它?即使它应该很直接,但这还是相当不清楚的。 - Kevin C
搞定了(已提交部署细节);我唯一需要进行的脚本修改是:使用return context.failure(AuthenticationFlowError.CLIENT_DISABLED);而不是使用自定义函数denyAccess(会导致默认主题出现问题)。 - Skyr

8

关于Allan的答案,他的方法是有效的(至少对我来说是;-)),但是我在部署方面遇到了一些困难。 这是我的做法:

  • 按照此处所述打包脚本到一个JAR文件中,并通过将其复制到standalone/deployments/来进行部署(请参阅手册链接)
  • 启用脚本:使用-Dkeycloak.profile.feature.scripts=enabled参数启动Keycloak
  • 在您的领域中,创建一个新流程。 在所需的子流程中复制浏览器流程,并将脚本身份验证器添加为最终(必需)元素: 流程配置截图
  • 现在,将feature:authenticate客户端角色分配给所有应受限制的客户端。不具备该角色的用户将无法访问应用程序。

嗨,我正在使用这种方法,但在脚本中用户为空,你知道为什么即使已经登录它也无法传递用户吗? - Mohammad
我的错误在于我没有复制整个浏览器流程,而是仅在所需角色执行时包含了子流程。无论如何还是谢谢! - Mohammad
我曾经使用过Javascript认证器来部署脚本,但是现在它们已经被弃用了(https://www.keycloak.org/docs/latest/server_admin/#script-authenticator)。如果您使用较新版本的KC,则您的方法是正确的(https://www.keycloak.org/docs/latest/server_development/#_script_providers)。 - Allan
第三步:是这样吗?我们应该创建一个空的流程(不是复制现有的流程),然后创建一个名为“Browser Flow”的空执行,逐个创建类似于“Browser”中的子流程?也就是说,在Keycloak中没有批量执行此操作的“复制”功能?谢谢。 - Alireza
@Alireza 至少在我做这件事的时候,没有(或者我没有找到;-)) - Skyr

6
我是这样解决的:
  1. 在Keycloak中创建新角色。
  2. 将该角色分配给组别。
  3. 在Kycloak中创建新的认证脚本。配置登录时允许哪些角色(例如,user.hasRole(realm.getRole("yourRoleName")))。
  4. 在客户端设置中,在“身份验证流覆盖”下选择刚刚创建的认证脚本。

2
如何在管理控制台中添加新的自定义认证器脚本? - cdan
Keycloak->realm->authentication->创建新的认证。一旦您配置完成,在“flows”下,您可以“添加执行”,然后从列表中选择脚本。 - lukasell
4
@lukasell,你的导航不够清晰,不太明确应该点击哪里。你能否改进一下你的评论? - Kevin C
无法按照步骤进行。 - ampc
此脚本在 KeyCloak 10.x 和 11.x 中始终以“null没有'hasRole'函数”结束。 - Bruno
嗨,对于Keycloak 21版本,我该如何将脚本添加到流程中? - Dyn amo

4

这个和其他答案中的脚本在Keycloak 9上不起作用,即使用户已登录,我也会得到空用户。 - Mohammad

2

我尝试了Allan的解决方案,并且在使用Keycloak 11.0.3时它运行良好,但它具有以下一些缺点。这是我的解决方案,用于认证脚本,如果用户不是至少属于给定组之一的成员,则不授予其访问权限。在这种情况下,会显示一个独特的错误消息。

AuthenticationFlowError = Java.type("org.keycloak.authentication.AuthenticationFlowError");

 function authenticate(context) {
    var allowed_groups = ['foo', 'bar'];
    var username = user ? user.username : "anonymous";
    var groups = user.getGroups();
    var group_array = groups.toArray();
    
    for (var i in group_array) {
        var gn = group_array[i].getName();
        if (allowed_groups.indexOf(gn) >= 0) {
            LOG.info("Access granted for user '" + username + "' for being member of LDAP group '" + gn + "'");
            return context.success();
        }    
    }

    LOG.info("Access denied for user '" + username + ". for not being member of any of the following LDAP groups: " + allowed_groups);
    context.failure(AuthenticationFlowError.IDENTITY_PROVIDER_DISABLED, context.form().setError(
        "User doesn't have the required LDAP group membership to view this page", null).createForm("error.ftl"));
    return;
 }

这种解决方案存在两个与用户体验相关的小缺点值得一提:
  • 当未登录的用户尝试连接到由认证脚本拒绝访问权限的客户端时,整个身份验证流程被视为失败。这意味着即使用户提供了正确的凭据,他们也无法登录Keycloak。
  • 当已登录的用户尝试连接到由认证脚本拒绝访问权限的客户端时,会显示Keycloak登录页面(而不显示任何错误消息),这是具有欺骗性的,因为用户可能会误以为他们未登录。
此外,如果您维护多个客户端,并且需要每个客户端检查不同的组(或角色),则必须实现尽可能多的新身份验证流程。简而言之,该解决方案有效,但它也有一些缺点。我认为基于组或角色限制访问是身份和访问管理系统中至关重要的功能,应该得到本地支持!

这种解决方案还有另一个缺点。您必须将表单身份验证类型设置为“必需”,否则脚本中的用户变量将为空。通过这样做,用户被迫每次(对于每个客户端)进行身份验证,因此它不再是SSO解决方案。 - András Gábor Soós
提供了一种可行的解决方案,可以消除上述缺点,从而保留SSO功能:https://stackoverflow.com/questions/65331061/user-attribute-based-web-service-access-control-by-keycloak - András Gábor Soós

2

2021年 - Keycloak 7.4.1.GA

我针对 SAML2 的解决方法如下:

  1. 添加新的认证流程(只需复制现有的流程)

  2. 添加“组访问观察器”执行并将其设置为必需

  3. 组访问观察器行上进行操作->配置

  4. 填写组名称

  5. 转到您的客户端,并将认证流程更改为新创建的流程。

最好的祝愿

示例认证流程

配置示例


2
使用 Keycloak >= 13.x 版本,您可能希望尝试具有条件的 "允许/拒绝访问" 认证器。 您可以将角色分配给组,并基于该角色构建条件。
如果这还不够灵活,请尝试我构建的 this 库来解决此问题。

2
根据文档https://www.keycloak.org/docs/6.0/server_admin/#executions的说明,您需要激活该功能才能使用“添加执行”来添加一些自定义脚本。请注意保留HTML标签。
bin/standalone.sh|bat -Dkeycloak.profile.feature.scripts=enabled

@Allan的解决方案中,带有“身份验证”功能,我觉得很不错。


功能:在另一个应用程序中存在会话的情况下,身份验证无法正常工作。例如:尝试连接到帐户应用程序,然后连接到您的应用程序。 - Abdourahmane FALL
从Keycloak 8开始,还必须启用脚本上传功能。 - Aurelia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接