mainick/keycloak-client-bundle

为 Symfony 设计的 Keycloak 客户端包,旨在简化将 Keycloak 集成到您的应用程序中,并提供额外的令牌管理和用户信息访问功能

安装: 746

依赖关系: 0

建议者: 0

安全性: 0

星级: 21

关注者: 2

分支: 7

开放问题: 5

类型:symfony-bundle

2.1.2 2024-05-26 10:18 UTC

README

Latest Version Software License Total Downloads

KeycloakClientBundle 包是一个针对 stevenmaguire/oauth2-keycloak 包的包装器,旨在简化在 Symfony 中将 Keycloak 集成到您的应用程序中,并提供额外的令牌管理和用户信息访问功能。它还包括一个监听器,用于在每次请求时验证令牌。

配置

在安装此包之前,您需要手动进行配置。您可以通过在项目的 config/packages 目录中创建一个 mainick_keycloak_client.yaml 文件并添加以下配置来实现这一点

# config/packages/mainick_keycloak_client.yaml

mainick_keycloak_client:
  keycloak:
    verify_ssl: '%env(bool:IAM_VERIFY_SSL)%'
    base_url: '%env(IAM_BASE_URL)%'
    realm: '%env(IAM_REALM)%'
    client_id: '%env(IAM_CLIENT_ID)%'
    client_secret: '%env(IAM_CLIENT_SECRET)%'
    redirect_uri: '%env(IAM_REDIRECT_URI)%'
    encryption_algorithm: '%env(IAM_ENCRYPTION_ALGORITHM)%'
    encryption_key: '%env(IAM_ENCRYPTION_KEY)%'
    encryption_key_path: '%env(IAM_ENCRYPTION_KEY_PATH)%'
    version: '%env(IAM_VERSION)%'

此外,建议您将以下环境变量添加到项目的环境文件中(例如,.env.env.local),并使用您的配置值替换适当的值

###> mainick/keycloak-client-bundle ###
IAM_VERIFY_SSL=true # Verify SSL certificate
IAM_BASE_URL='<your-base-server-url>'  # Keycloak server URL
IAM_REALM='<your-realm>' # Keycloak realm name
IAM_CLIENT_ID='<your-client-id>' # Keycloak client id
IAM_CLIENT_SECRET='<your-client-secret>' # Keycloak client secret
IAM_REDIRECT_URI='<your-redirect-uri>' # Keycloak redirect uri
IAM_ENCRYPTION_ALGORITHM='<your-algorithm>' # RS256, HS256, etc.
IAM_ENCRYPTION_KEY='<your-public-key>' # public key
IAM_ENCRYPTION_KEY_PATH='<your-public-key-path>' # public key path
IAM_VERSION='<your-version-keycloak>' # Keycloak version
###< mainick/keycloak-client-bundle ###

请确保用您的实际配置值替换占位符值。配置完包和环境变量后,您可以继续进行安装。

安装

您可以使用 Composer 安装此包

composer require mainick/keycloak-client-bundle

然后,通过将其添加到项目的 config/bundles.php 文件中注册的包列表中,启用该包

// config/bundles.php

return [
    // ...
    Mainick\KeycloakClientBundle\MainickKeycloakClientBundle::class => ['all' => true],
];

通过在安装之前配置包,您确保它在安装后即可使用。

用法

获取 Keycloak 客户端

您可以通过将 Mainick\KeycloakClientBundle\Interface\IamClientInterface 接口注入到您的控制器或服务中来获取 Keycloak 客户端。

要使用它,您需要将以下配置添加到您的 config/services.yaml 文件中

services:
    Mainick\KeycloakClientBundle\Interface\IamClientInterface:
        alias: Mainick\KeycloakClientBundle\Provider\KeycloakClient

然后,您可以在控制器或服务中使用它

<?php

declare(strict_types=1);

namespace App\Service;

use Mainick\KeycloakClientBundle\Interface\IamClientInterface;

class IamService
{
    public function __construct(
        private IamClientInterface $iamClient
    ) {
    }
}

执行所需的操作,例如检索额外的用户声明、分配的角色、关联的组等。

// authenticate the user with username and password
$accessToken = $this->iamClient->authenticate($username, $password);

// authenticate the user with authorization code
$accessToken = $this->iamClient->authenticateCodeGrant($authorizationCode);

// verify and introspect the token
$userRepresentation = $this->iamClient->verifyToken($accessToken);
echo $userRepresentation->id; // id
echo $userRepresentation->username; // username
echo $userRepresentation->email; // email
echo $userRepresentation->firstName; // first name
echo $userRepresentation->lastName; // last name
echo $userRepresentation->name; // full name
echo $userRepresentation->groups; // all groups assigned to the user
echo $userRepresentation->realmRoles; // realm roles assigned to the user
echo $userRepresentation->clientRoles; // client roles assigned to the user
echo $userRepresentation->applicationRoles; // specific client roles assigned to the user
echo $userRepresentation->attributes; // additional user attributes

// refresh the token
$accessToken = $this->iamClient->refreshToken($accessToken);

// get user info
$userInfo = $this->iamClient->userInfo($accessToken);
echo $userInfo->id; // id
echo $userInfo->username; // username
echo $userInfo->email; // email
echo $userInfo->firstName; // first name
echo $userInfo->lastName; // last name
echo $userInfo->name; // full name
echo $userInfo->groups; // all groups assigned to the user
echo $userInfo->realmRoles; // realm roles assigned to the user
echo $userInfo->clientRoles; // client roles assigned to the user
echo $userInfo->applicationRoles; // specific client roles assigned to the user
echo $userInfo->attributes; // additional user attributes

// has role
$hasRole = $this->iamClient->hasRole($accessToken, $roleName);

// has any role
$hasAnyRole = $this->iamClient->hasAnyRole($accessToken, $roleNames);

// has all roles
$hasAllRoles = $this->iamClient->hasAllRoles($accessToken, $roleNames);

// has group
$hasGroup = $this->iamClient->hasGroup($accessToken, $groupName);

// has any group
$hasAnyGroup = $this->iamClient->hasAnyGroup($accessToken, $groupNames);

// has all groups
$hasAllGroups = $this->iamClient->hasAllGroups($accessToken, $groupNames);

// has scope
$hasScope = $this->iamClient->hasScope($accessToken, $scopeName);

// has any scope
$hasAnyScope = $this->iamClient->hasAnyScope($accessToken, $scopeNames);

// has all scopes
$hasAllScopes = $this->iamClient->hasAllScopes($accessToken, $scopeNames);

令牌验证监听器

KeycloakClientBundle 包含一个内置的监听器,TokenAuthListener,它会在每次请求上自动验证 JWT 令牌,确保 Keycloak 集成的安全性和有效性。此监听器无缝处理令牌验证,让您可以专注于应用程序的逻辑。

使用 TokenAuthListener

在您的 Symfony 项目中,将 TokenAuthListener 添加到您的 config/services.yaml 文件中作为已注册的服务,并将其标记为 kernel.event_listener。这将使监听器在每次请求上触发。

services:
    Mainick\KeycloakClientBundle\EventSubscriber\TokenAuthListener:
        tags:
          - { name: kernel.event_listener, event: kernel.request, method: checkValidToken, priority: 0 }

检索用户信息

此外,TokenAuthListener 将一个 user 属性添加到 Symfony 请求对象中,该属性包含 UserRepresentationDTO 对象。

// get the user object from the request
$user = $request->attributes->get('user');

user 属性包含从 JWT 令牌中检索的用户信息,并是一个 UserRepresentationDTO 类的实例。这使得您的应用程序在处理请求时可以轻松访问与用户相关的数据。

排除令牌验证的路由

默认情况下,TokenAuthListener 验证所有传入请求的令牌。但是,如果您有特定的路由需要排除令牌验证,可以使用 ExcludeTokenValidationAttribute 属性来实现。

要为特定路由排除令牌验证,请将 ExcludeTokenValidationAttribute 应用到相应的控制器方法上。

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Mainick\KeycloakClientBundle\Annotation\ExcludeTokenValidationAttribute;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class MyController extends AbstractController
{
    #[Route("/path/to/excluded/route", name: "app.excluded_route", methods: ["GET"])]
    #[ExcludeTokenValidationAttribute]
    public function excludedRouteAction(): Response
    {
        // This route is excluded from token validation.
        // ...
    }
}

当将 ExcludeTokenValidationAttribute 应用于方法时,TokenAuthListener 将跳过对该特定路由的请求的令牌验证。

Symfony 安全配置

包配置

要使用 KeycloakClientBundle 与 Symfony 的安全组件,您需要配置安全系统以使用 Keycloak 客户端。

首先,您需要向包配置文件中添加一个新部分

# config/packages/mainick_keycloak_client.yaml
security:
  default_target_route_name: '%env(TARGET_ROUTE_NAME)%'

然后,您需要将 Keycloak 重定向 URI 配置为 mainick_keycloak_security_auth_connect_check 包路由,该路由在成功登录后重定向到默认路由或引用路由。

建议将以下环境变量更改为您项目的环境文件(例如,.env.env.local)中的 URI。相同的 URI 必须在 Keycloak 应用程序客户端中进行配置

###> mainick/keycloak-client-bundle ###
IAM_REDIRECT_URI='https://app.local/auth/keycloak/check'
TARGET_ROUTE_NAME=app_home
###< mainick/keycloak-client-bundle ###

以下是完整的配置文件

# config/packages/mainick_keycloak_client.yaml

mainick_keycloak_client:
  keycloak:
    verify_ssl: '%env(bool:IAM_VERIFY_SSL)%'
    base_url: '%env(IAM_BASE_URL)%'
    realm: '%env(IAM_REALM)%'
    client_id: '%env(IAM_CLIENT_ID)%'
    client_secret: '%env(IAM_CLIENT_SECRET)%'
    redirect_uri: '%env(IAM_REDIRECT_URI)%'
    encryption_algorithm: '%env(IAM_ENCRYPTION_ALGORITHM)%'
    encryption_key: '%env(IAM_ENCRYPTION_KEY)%'
    encryption_key_path: '%env(IAM_ENCRYPTION_KEY_PATH)%'
    version: '%env(IAM_VERSION)%'
security:
  default_target_route_name: '%env(TARGET_ROUTE_NAME)%'

路由配置

config/routes/ 中创建一个新文件以加载预配置的包路由。

# config/routes/mainick_keycloak_security.yaml
mainick_keycloak_security_auth_connect:
  path:       /auth/keycloak/connect
  controller: Mainick\KeycloakClientBundle\Controller\KeycloakController::connect

mainick_keycloak_security_auth_connect_check:
  path:       /auth/keycloak/check
  controller: Mainick\KeycloakClientBundle\Controller\KeycloakController::connectCheck

mainick_keycloak_security_auth_logout:
  path:       /auth/keycloak/logout
  controller: Mainick\KeycloakClientBundle\Controller\KeycloakController::logout

安全配置

然后,您需要配置安全系统以使用 Keycloak 客户端。您可以通过向您的 config/packages/security.yaml 文件中添加以下配置来实现这一点,以使用包的 UserProvider

# config/packages/security.yaml
providers:
  mainick_keycloak_user_provider:
    id: Mainick\KeycloakClientBundle\Security\User\KeycloakUserProvider

这是一个简单的配置,仅允许具有“ROLE_USER”或“ROLE_ADMIN”角色的用户访问 /app/* 路由

# config/packages/security.yaml
security:
  providers:
    mainick_keycloak_user_provider:
      id: Mainick\KeycloakClientBundle\Security\User\KeycloakUserProvider

  firewalls:
    dev:
      pattern: ^/(_(profiler|wdt)|css|images|js)/
      security: false

    auth_connect:
      pattern: /auth/keycloak/connect
      security: false

    secured_area:
      pattern: ^/
      provider: mainick_keycloak_user_provider
      entry_point: Mainick\KeycloakClientBundle\Security\EntryPoint\KeycloakAuthenticationEntryPoint
      custom_authenticator:
        - Mainick\KeycloakClientBundle\Security\Authenticator\KeycloakAuthenticator
      logout:
        path: mainick_keycloak_security_auth_logout

  role_hierarchy:
    ROLE_ADMIN: ROLE_USER

  # Easy way to control access for large sections of your site
  # Note: Only the *first* access control that matches will be used
  access_control:
    - { path: ^/app, roles: ROLE_ADMIN }

注销

要注销用户,可以使用以下代码

use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Annotation\Route;
use Mainick\KeycloakClientBundle\Annotation\ExcludeTokenValidationAttribute;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class MyController extends AbstractController
{
    #[Route("/logout", name: "app.logout", methods: ["GET"])]
    public function logout(): RedirectResponse
    {
        return $this->redirectToRoute('mainick_keycloak_security_auth_logout');
    }
}

或在您的 twig 模板中创建一个链接

<a href="{{ path('mainick_keycloak_security_auth_logout') }}">Logout</a>

这将使用户重定向到 Keycloak 注销页面,用户将在 Keycloak 服务器上注销。

登录后的重定向

要登录后将用户重定向到特定路由,可以将 TARGET_ROUTE_NAME 环境变量设置为所需的路由名称。

###> mainick/keycloak-client-bundle ###
TARGET_ROUTE_NAME=app_home
###< mainick/keycloak-client-bundle ###

这将使用户在成功登录后重定向到 app_home 路由。

故障排除 - 浏览器中的访问被拒绝

如果您在浏览器中遇到“访问被拒绝”错误,可能是因为作用域角色配置不正确。

更正方法

  1. 检查是否已为应用程序客户端创建了 ROLE_ADMINROLE_USER 角色。
  2. 在左侧面板中单击 Client scopes,然后单击 roles
  3. 单击 Mappers 选项卡,然后单击 client roles
  4. 禁用 Add to userinfo,单击 Save,然后启用 Add to userinfo 并单击 Save

请检查 Keycloak 中分配给用户的角色和在 Symfony 安全配置中配置的角色。

运行测试

安装 Composer 依赖关系

git clone https://github.com/mainick/KeycloakClientBundle.git
cd KeycloakClientBundle
composer update

然后运行测试套件

composer test

作者

许可

MIT 许可证(MIT)。有关更多信息,请参阅 许可文件

贡献

我们欢迎您的贡献!如果您希望增强此包或发现了一个错误,请随意创建一个 pull request 或在 问题跟踪器 中报告一个问题。

有关详细信息,请参阅 CONTRIBUTING