【问题标题】:Keycloak authenticated client of one application couldn't access another client application in same realm一个应用程序的 Keycloak 身份验证客户端无法访问同一领域中的另一个客户端应用程序
【发布时间】:2021-12-02 13:53:41
【问题描述】:

我使用 Keycloak 作为身份提供者。我有一个 springboot-mvc-client 应用程序,它注册为机密客户端并配置了 authorization_code 授权类型。当我访问应用程序时,我被重定向到 Keycloak 登录页面,成功登录后,我可以看到我的 springboot-mvc-client 页面。 一切正常。

现在我有了另一个 springboot-rest-api,它是一个 ResourceServer。我已将 application.properties 配置如下:

server.port=8182
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8181/auth/realms/my-realm

现在我想从我的 springboot-mvc-client 访问 springboot-rest-api 端点。我捕获了 AccessToken 并使用 RestTemplate 作为 Authorization 标头传递,它也可以正常工作。

问题: 如果我在 keycloak 中将 springboot-rest-api 应用程序注册为具有 confidential 访问类型的客户端,并尝试从 springboot-mvc-client 访问 API > 然后 API 调用失败,响应是登录页面的 HTML 内容。

我的理解(可能是错误的)是因为两个客户端都在同一个领域注册,所以我应该能够使用从 springboot-mvc-client 身份验证获得的身份验证令牌调用 springboot-rest-api 端点.

额外观察是如果我将 springboot-rest-api 访问类型从 confidential 更改为 bearer-only然后它工作正常。另外,我可以使用 client_credentials 授权获取 springboot-rest-api 的 accessToken 并使用它我可以调用 springboot-rest-api 端点。

问题是:使用一个机密客户的访问令牌我们不能访问另一个机密客户的资源,即使他们在同一个领域?

PS: 更新 1:

将 OAuth 安全配置添加到 springboot-rest-api 它工作正常。 即,通过使用从 springboot-mvc-client 获得的相同 AccessToken 我能够调用 springboot-rest-api 端点。

我在 springboot-rest-api 中添加了以下配置:

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .cors()
                .and()
                .csrf()
                .disable()
                .oauth2ResourceServer()
                .jwt();
    }
}

更新 2: 只是分享另一个观察结果。

当 springboot-rest-api 被配置为只是一个在其 application.properties 文件中带有spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8181/auth/realms/my-realm 的 ResourceServer 时,则不需要上面提到的 SecurityConfig 配置。但是如果我想让 springboot-rest-api 同时充当 OAuth Client + ResourceServer,那么我需要像上面提到的那样配置 SecurityConfig

【问题讨论】:

    标签: spring-boot oauth-2.0 keycloak spring-security-oauth2


    【解决方案1】:

    简而言之。 OAuth2 定义了客户端(请求对受保护资源的授权访问)和资源服务器(托管受保护资源)之间的交互。通过访问令牌授予访问权限。一个客户端与另一个客户端之间没有交互。


    我认为它有助于根据 OAuth2 角色而非应用程序进行推理,因为不一定存在一对一的映射。

    来自OAuth 2.1 specs

    • 客户。代表资源所有者并经其授权发出受保护资源请求的应用程序。
    • 资源服务器。托管受保护资源的服务器,能够接受和响应受保护资源请求 使用访问令牌。

    让我们首先考虑 OAuth2 授权代码流程

    OAuth2 客户端由用户授权代表他们访问由 OAuth2 资源服务器托管的受保护资源。通过访问令牌授予访问权限。如果 springboot-mvc-client 是 OAuth2 客户端并且需要 springboot-rest-api 代表用户,那么 springboot-rest-api 将是根据 OAuth2 授权代码流的 OAuth2 资源服务器。

    OAuth2 客户端不是访问令牌的接收者。他们按原样从 OAuth2 授权服务器获取它们并将它们转发到 OAuth2 资源服务器以获得对受保护资源的授权访问。 OAuth2 资源服务器是这些访问令牌的接收者,它们负责读取并验证它们。因此,OAuth2 客户端不会使用这样的访问令牌来获得对另一个 OAuth2 客户端的授权访问,因为后者不会读取访问令牌也不会验证其签名。这是纯粹的 OAuth2 角色。


    如果 springboot-rest-api 要求对用户进行身份验证并代表该用户执行请求,那么我们谈论的是 OAuth2 客户端角色。但在前面的场景中,它已经扮演了 OAuth2 资源服务器的角色。因此,它必须扮演两个角色(当用户直接调用时为 OAuth2 客户端,当 springboot-mvc-client 使用访问令牌代表用户调用时为 OAuth2 资源服务器)。那时,我可能会考虑引入某种边缘服务作为系统的入口点是否有意义(例如,使用 Spring Cloud Gateway),以便边缘服务扮演 OAuth2 客户端角色并验证用户,而系统中的其他应用程序将是 OAuth2 资源服务器。

    如果我们代表用户谈论授权访问,这是有效的。如果您正在寻找 springboot-mvc-client 代表其(而非用户)调用 springboot-rest-api 的解决方案,那么应该使用 OAuth2 Client Credentials,并且不会有任何基于浏览器的登录流程。这是一个通常用于服务到服务通信(不涉及用户)的流程。不过,我们仍然会讨论 OAuth2 客户端和 OAuth2 资源服务器。在 Okta 博客上有一个很好的 article


    OAuth2 框架中没有定义两个 OAuth2 客户端之间的流程,因为定义 OAuth2 客户端的是它想要对受保护资源发出请求,这些资源根据定义托管在 OAuth2 资源服务器上。

    但是,您可以有两个应用程序,其中第一个扮演 OAuth2 客户端的角色,第二个扮演 OAuth2 客户端和 OAuth2 资源服务器的角色,具体取决于所涉及的交互。


    最后一点,您可能会发现以下关于 Keycloak 支持的客户端类型的有用信息(来自 docs)。

    • 机密。机密访问类型适用于需要执行浏览器登录并在将访问代码转换为访问令牌时需要客户端密码的服务器端客户端。 [...] 这种类型应该用于服务器端应用程序。

    • public 公共访问类型适用于需要执行浏览器登录的客户端客户端。对于客户端应用程序,无法保证机密安全。相反,通过为客户端配置正确的重定向 URI 来限制访问非常重要。

    • 仅承载。 Bearer-only 访问类型意味着应用程序只允许不记名令牌请求。如果开启此功能,此应用程序将无法参与浏览器登录。

    【讨论】:

    • 非常感谢 Thomas 的详细解释。在通过文档和其他一些教程/演示文稿之后,我意识到我错过了导致问题的 springboot-rest-api 的安全配置。添加安全配置以启用 OAuth ResourceServer 后,它工作正常。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-30
    • 2011-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-30
    • 1970-01-01
    相关资源
    最近更新 更多