当后端在 Docker 容器中运行时,Keycloak 令牌验证失败

the*_*pan 8 oauth-2.0 docker keycloak

我正处于构建网络应用程序的早期阶段。我打算使用 Keycloak 作为身份提供者来保护后端。在我的本地计算机上,我将 Keycloak 和后端都作为 docker 容器运行,但在不同的网络上,因为最终在生产中,我希望运行 Keycloak 的身份验证服务器与后端分别运行,例如account.example.comapi.example.com分别

在本地,我的 Keycloak 容器可以通过基本 URL 访问http://localhost:8080/auth,后端通过http://test.localhost:8000/

我在 Keycloak 领域创建了一个客户端,其访问类型是机密的。我正在使用授权代码授予类型生成令牌。在此输入图像描述

因此,后端的每个 REST API 端点都会验证传递到授权标头的令牌,然后在处理请求之前调用 Keycloak 服务器来验证令牌。

我目前遇到的问题是令牌验证失败并响应

{"error":"invalid_token","error_description":"Token verification failed"}'
Run Code Online (Sandbox Code Playgroud)

经过调查,显然,这是因为我从后端 API 容器调用 Keycloak 服务器。如果我在后端 docker 容器内使用curl 生成令牌,则我收到的令牌可以正常验证,但在容器外部生成的令牌则不能。

我用作python-keycloakKeycloak REST API 的包装器

from keycloak import KeycloakOpenID


self._keycloak = KeycloakOpenID(
            server_url='http://host.docker.internal:8080/auth/',
            realm_name='myrealm',
            client_id='myclient',
            client_secret_key='mysecret,
        )

if "HTTP_AUTHORIZATION" not in request.META:
       
        return JsonResponse(
            {"detail": NotAuthenticated.default_detail},
            status=NotAuthenticated.status_code,
        )

 auth_header = request.META.get("HTTP_AUTHORIZATION").split()
 token = auth_header[1] if len(auth_header) == 2 else auth_header[0]

    
    try:
        self.keycloak.userinfo(token)
    except KeycloakInvalidTokenError as e:
        # print(e)
        return JsonResponse(
            {"detail": AuthenticationFailed.default_detail},
            status=AuthenticationFailed.status_code,
        )
Run Code Online (Sandbox Code Playgroud)

如何解决此问题并在本地计算机上进行令牌验证

sve*_*ben 13

您的令牌无效,因为iss令牌中的颁发者 ( ) 与您的后端服务期望的颁发者不匹配。

您的后端(或后端中的适配器/框架)将使用 OIDC 发现协议来确定预期的发行者。为此,它将调用https://keycloak-container-name/auth/realms/<your-realm>/.well-known/openid-configuration. 这将返回如下元数据:

{
  "issuer":"https://keycloak-container-name/auth/realms/<your-realm>",
  ...
}
Run Code Online (Sandbox Code Playgroud)

keycloak-container-nameKeycloak将根据请求确定发行者的主机部分(在本例中)。因此,如果您的后端从 docker 网络内查询发现端点keycloak-container-name,则主机部分将为your-container-name. 您的后端将期望发行人处于https://keycloak-container-name/auth/realms/<your-realm>这种情况。

现在,如果您想从前端查询令牌,您的前端会将请求发送到http://localhost:8080/auth/.... 由于发行者将根据请求确定,因此该令牌中的发行者将是https://localhost:8080/auth/realms/<your-realm>这种情况。这与预期的发行者不匹配https://keycloak-container-name/auth/realms/<your-realm>,因此令牌将被视为无效而被拒绝。

您还可以通过调用 OIDC 发现端点来验证这一点http://localhost:8080/auth/realms/<your-realm>/.well-known/openid-configuration。您将得到如下响应:

{
  "issuer":"http://localhost:8080/auth/realms/<your-realm>",
  ...
}
Run Code Online (Sandbox Code Playgroud)

要解决此问题,您可以将Frontend URL您的领域设置为http://localhost:8080/auth。通过此设置,发行人将不再在请求中确定,而是被修复http://localhost:8080/auth/realms/<your-realm>。您可以通过向 发出请求,从后端容器中检查这一点https://keycloak-container-name/auth/realms/<your-realm>/.well-known/openid-configuration。现在将返回如下元数据:

{
  "issuer":"http://localhost:8080/auth/realms/<your-realm>",
  ...
}
Run Code Online (Sandbox Code Playgroud)

如果您不想为每个领域单独配置此选项,则可以在服务器范围内配置默认主机名提供程序。