Keycloak注销不会结束会话

Keycloak logout does not end session

我在 Java 应用程序中使用 Keycloak 3.4,使用 Spring 框架和带有 Keycloak Jetty-81-Adapter 3.4 的 Jetty 8.1。

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

You can log out of a web application in multiple ways. For Java EE servlet containers, you can call HttpServletRequest.logout()..

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

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

For other browser applications, you can redirect the browser to
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 实现如此不同以至于它根本无法工作?

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

中的错误

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

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

查看以下文档: “

server passing the refresh token. If the method is executed from an unprotected page (a page that does not check for a valid token) the refresh token can be unavailable and, in that case, the adapter skips the call. For this reason, using a protected page to execute HttpServletRequest.logout() is recommended so that current tokens are always taken into account and an interaction with the {project_name} server is performed if needed.

如果方法(注销)是从不受保护的页面(不检查有效令牌的页面)执行的,则刷新令牌可能不可用,在这种情况下,适配器会跳过调用。出于这个原因,建议使用受保护的页面来执行 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());
}