Keycloak 注销不会结束会话

Posted

技术标签:

【中文标题】Keycloak 注销不会结束会话【英文标题】:Keycloak logout does not end session 【发布时间】:2018-08-17 20:27:06 【问题描述】:

我在使用 Spring Framework 和 Jetty 8.1 和 Keycloak Jetty-81-Adapter 3.4 的 Java 应用程序中使用 Keycloak 3.4。

根据 Keycloak 文档,我应该能够在 Java EE 应用程序中使用 HttpServletRequest 从 Keycloak 注销。但是,这在我的情况下不起作用,即使 Jetty 支持 HttpServletRequests。

您可以通过多种方式退出 Web 应用程序。对于 Java EE servlet 容器,可以调用 HttpServletRequest.logout()..

如果我尝试以这种方式注销,我会被重定向到 keycloak(登录屏幕,可选择从多个领域登录中进行选择)。然而,当我选择我的首选领域时,我会立即再次登录到 Web 应用程序,而无需提供任何凭据。

我尝试了另一种方法,重定向到 Keycloak:

对于其他浏览器应用,您可以将浏览器重定向到http://auth-server/auth/realms/realm-name/protocol/openid-connect/logout?redirect_uri=encodedRedirectUri

但它会在 Keycloak 日志中引发连接被拒绝错误,因为 Keycloak 尝试以直接方式调用我的 webapp。它对保持活动状态的 Keycloak 会话没有影响。我强烈不喜欢直接从 Keycloak 到 webapp 的服务器端调用。

有什么建议为什么 HttpServletRequest.logout() 在我的情况下不会破坏 keycloak 会话? HttpServletRequest 的 Jetty 实现是否与 Java EE 实现如此不同以至于它根本无法工作?

【问题讨论】:

【参考方案1】:

在使用远程 (OIDC) 身份提供商时,我也有过类似的经历。我发现HttpServletRequest.logout 确实破坏了Keycloak 中的会话,但没有传播到我的远程身份提供者的注销网址。当转到远程登录站点时,它只是立即将我重定向回来,看到我有一个活动会话。这看起来很像 keycloak 会话实际上并没有失效,但我检查了一下,确实如此。使用浏览器重定向到 url 在这两个地方都注销了。可能是Keycloak 中的一个错误。

难道你也面临同样的问题?要验证,请尝试注销,然后在 keycloak 中选择您的客户端并列出会话,看看它是否仍然存在于 Keycloak 中。

【讨论】:

这是一个很棒的建议!通过在注销过程中包含 idp,整个过程都可以正常工作。 您能详细说明一下吗?您是否真的使用 HttpServletRequest.logout 在外部 idp 中实现了注销?如果是,你做了什么? 您甚至可以就您所做的事情提供您自己的答案,因为我的回答更多的是关于下一步看哪里的建议,而不是具体的解决方案.. 我有同样的东西。我在我的注销 servlet 中以这种方式解决了它:request.logout(); response.sendRedirect(request.getContextPath());。我在 keycloak 示例中找到了这个【参考方案2】:

您需要将包含“request.logout()”操作的 Servlet 或 Rest 作为受保护的资源,以便令牌可用:

查看以下文档: "

服务器传递刷新令牌。如果该方法是从一个 不受保护的页面(不检查有效令牌的页面) 刷新令牌可能不可用,在这种情况下,适配器会跳过 通话。为此,使用受保护的页面来执行 建议使用 HttpServletRequest.logout() 以便当前令牌 始终考虑并与 project_name 进行交互 如果需要,执行服务器。

"

【讨论】:

【参考方案3】:

如果从不受保护的页面(不检查有效令牌的页面)执行方法(注销),则刷新令牌可能不可用,在这种情况下,适配器会跳过调用。因此,建议使用受保护的页面来执行 HttpServletRequest.logout(),以便始终考虑当前令牌,并在需要时执行与 Keycloak 服务器的交互。 https://www.keycloak.org/docs/latest/securing_apps/index.html#logout

这在受保护的页面中对我有用,但在不受保护的页面中不起作用

public ExternalContext currentExternalContext() 
        if (FacesContext.getCurrentInstance() == null) 
            throw new RuntimeException("message here ");
         else 
            return FacesContext.getCurrentInstance().getExternalContext();
         
    

public void logout() throws IOException, ServletException 
    HttpServletRequest request = (HttpServletRequest) currentExternalContext().getRequest();
    ExternalContext externalContext = currentExternalContext();
    request.logout();
    externalContext.redirect(externalContext.getRequestContextPath());

【讨论】:

以上是关于Keycloak 注销不会结束会话的主要内容,如果未能解决你的问题,请参考以下文章

Keycloak注销请求不会注销用户

带有弹簧靴的keycloak openid单次注销

如果 keycloak 会话过期,我如何重定向到登录页面?

Keycloak SSO 会话最大值 = 0?

使用 Spring Security Adapter 时的 Keycloak 会话超时行为

如何在基于 Keycloak/Spring 的应用程序中实现单点注销?