mainick / keycloak-client-bundle
为 Symfony 设计的 Keycloak 客户端包,旨在简化将 Keycloak 集成到您的应用程序中,并提供额外的令牌管理和用户信息访问功能
Requires
- php: >=8.2
- stevenmaguire/oauth2-keycloak: ^5.1
- symfony/framework-bundle: ^7.0
- symfony/http-kernel: ^7.0
- symfony/routing: ^7.0
- symfony/security-bundle: ^7.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.52
- mockery/mockery: ~1.5.0
- phpunit/phpunit: ^11.1
README
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
路由。
故障排除 - 浏览器中的访问被拒绝
如果您在浏览器中遇到“访问被拒绝”错误,可能是因为作用域角色配置不正确。
更正方法
- 检查是否已为应用程序客户端创建了 ROLE_ADMIN 和 ROLE_USER 角色。
- 在左侧面板中单击 Client scopes,然后单击 roles
- 单击 Mappers 选项卡,然后单击 client roles
- 禁用 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