Keycloak 桌面 java 适配器删除 KEYCLOAK_IDENTITY cookie

Posted

技术标签:

【中文标题】Keycloak 桌面 java 适配器删除 KEYCLOAK_IDENTITY cookie【英文标题】:Keycloak desktop java adapter deletes KEYCLOAK_IDENTITY cookies 【发布时间】:2021-10-30 05:14:09 【问题描述】:

我遇到了 Keycloak Java 适配器的问题。我尝试将桌面应用程序与 Keycloak 集成,并在其他一些 Web 应用程序之间启用 SSO。问题是,当我尝试登录 Keycloak 时,一切正常且顺利,我获得有关正确身份验证的信息,获取令牌,甚至可以毫无问题地解析它,但是在 WebBrowser 中没有创建会话(没有会话,没有 cookie) .这意味着我不能将刚刚创建的会话与同一 Keycloak 领域中的其他应用程序一起使用,即使 Keycloak 中的会话已正确创建。

在使用我的桌面适配器“成功”登录后,其他应用程序之前创建和存储的更多 cookie 也会被删除(作为 cookie,我的意思是 KEYCLOAK_IDENTITY 和 KEYCLOAK_INDENTITY_LEGACY)。当我检查浏览器 cookie 时,有一些警告声明说 cookie 因过期而被拒绝。

我使用的是 KeycloakInstalled 适配器(最新,15.0.2 版本)。我使用页面上的说明配置它:https://www.keycloak.org/docs/latest/securing_apps/

我认为本案例中最重要的一段代码:

            KeycloakInstalled keycloak = new KeycloakInstalled();
            AdapterConfig config = new AdapterConfig();
            Map<String, Object> credentials = new HashMap<String, Object>();
            credentials.put("secret", secret);
            config.setAuthServerUrl(url);

            keycloak.getDeployment().setRealm(realm);
            keycloak.getDeployment().setAuthServerBaseUrl(config);
            keycloak.getDeployment().setResourceName(resource);
            keycloak.getDeployment().setResourceCredentials(credentials);
            keycloak.getDeployment().setClientAuthenticator(ClientCredentialsProviderUtils.bootstrapClientAuthenticator(keycloak.getDeployment()));

            keycloak.loginDesktop();

在这种情况下,一些 Keycloak 属性在 keycloak.json 文件中静态设置,而在 Java 中一些动态设置(上面的示例)。在 keycloak.json 文件中,一些属性如 realmauth-server-urlresource秘密充满了垃圾数据,只是因为它们是稍后动态设置的。


  "realm": "<realm>",
  "auth-server-url": "<url>",
  "ssl-required": "external",
  "resource": "<keycloak-client>",
  "use-resource-role-mappings": true,
  "credentials" : 
    "secret" : "abc"
  ,
  "truststore" : "<file>.jks",
  "truststore-password" : "<password>"

Keycloak 的客户端配置我是这样设置的:

Keycloak_configuration

如何避免使用我的桌面适配器删除会话 cookie?

【问题讨论】:

【参考方案1】:

我已经解决了我的问题。一切都与适配器身份验证机制有关。它是这样工作的:

    适配器连接到 keycloak 以验证用户身份。 然后从标记为 redirect_url 的第一个 url 重定向到参数中的地址,以告诉适配器身份验证是肯定的。 请注意,到目前为止,每个 cookie 都已正确设置。 然后有另一个重定向到 /delegated 端点,这会使所有会话 cookie 过期。这一切都归功于 Keycloak 存储库中的这段代码:
    @Path("delegated")
    public Response kcinitBrowserLoginComplete(@QueryParam("error") boolean error) 
        AuthenticationManager.expireIdentityCookie(realm, session.getContext().getUri(), clientConnection);
        AuthenticationManager.expireRememberMeCookie(realm, session.getContext().getUri(), clientConnection);
        ...
    

来源:https://github.com/keycloak/keycloak/blob/d29d945cc4f5674ecff58cf5bf6bb65933f65cad/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java#L295

我所做的是创建我自己的 CustomKeycloakInstalled 类扩展原始 KeycloakInstalled 我在其中复制了关键方法 loginDesktop() 和其他一些让 loginDesktop() 正常工作的必要方法 - 最重要的是,用我自己的方法覆盖返回最后一个重定向 url (/delegated) 的函数:

        private String getRedirectUrl() 
            String redirectUrl = CustomKeycloakInstalled.this.getDeployment().getTokenUrl()
                    .replace("/protocol/openid-connect/token", "/my-endpoint");
            if (this.error != null) 
                redirectUrl = redirectUrl + "?error=true";
            

            return redirectUrl;
        

现在每个 cookie 都已正确设置,SSO 运行良好,但 Keycloak 服务器中没有 /my-endpoint,因此最后一步是实施和部署它。我在下面使用了 KeycloakResourceProvider 示例: https://github.com/keycloak/keycloak/tree/master/examples/providers/rest

这真的很简单,所以我所做的就是用 Keycloak 的端点 delegated 的实现替换 get() 方法,但不会过期 cookie 片段:

    @GET
    @Produces("text/plain; charset=utf-8")
    public Response get(@QueryParam("error") boolean error) 
        if (error) 
            LoginFormsProvider forms = session.getProvider(LoginFormsProvider.class);
            return forms
                    .setAttribute("messageHeader", forms.getMessage(Messages.DELEGATION_FAILED_HEADER))
                    .setAttribute(Constants.SKIP_LINK, true).setError(Messages.DELEGATION_FAILED).createInfoPage();

         else 
            LoginFormsProvider forms = session.getProvider(LoginFormsProvider.class);
            return forms
                    .setAttribute("messageHeader", forms.getMessage(Messages.DELEGATION_COMPLETE_HEADER))
                    .setAttribute(Constants.SKIP_LINK, true)
                    .setSuccess(Messages.DELEGATION_COMPLETE).createInfoPage();
        
    

然后编译并部署在 Keycloak 中。

【讨论】:

以上是关于Keycloak 桌面 java 适配器删除 KEYCLOAK_IDENTITY cookie的主要内容,如果未能解决你的问题,请参考以下文章

Keycloak Java 适配器 - 检索角色列表

Keycloak - 令牌过期后在 Ajax 调用中获取 401

Keycloak Java Adapter 属性“public-client”是啥意思?

如何使用 Keycloak 解码 JWT

我应该明确验证 Keycloak 令牌还是由 Keycloak 适配器完成?

使用接收到的访问令牌检索 Keycloak 用户数据