a-mazalov / laravel-keycloak-guard
🔑 Laravel 的简单 Keycloak 守卫 (Fork)
Requires
- php: ^8.0
- firebase/php-jwt: ^6.3
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.10
- nunomaduro/collision: ^6.3
- orchestra/testbench: ^7.7
- pestphp/pest: ^1.22
- phpunit/phpunit: ^9.5
README
安装
composer require a-mazalov/laravel-keycloak-guard
简单 Keycloak 守卫 for Laravel
此包帮助您基于从 Keycloak 服务器 生成的 JWT 令牌在 Laravel API 上进行用户认证。
要求
✔️ 我正在使用 Laravel 构建一个 API。
✔️ 我将不会使用 Laravel Passport 进行认证,因为 Keycloak 服务器会完成这项工作。
✔️ 前端是一个独立的项目。
✔️ 前端用户直接在 Keycloak 服务器 上进行认证以获取 JWT 令牌。这个过程与 Laravel API 没有关系。
✔️ 前端保留从 Keycloak 服务器获取的 JWT 令牌。
✔️ 前端使用该令牌向 Laravel API 发起请求。
💔 如果您的应用程序不符合要求,您可能需要寻找 https://socialiteproviders.com/Keycloak 或 https://github.com/Vizir/laravel-keycloak-web-guard
流程
-
前端用户在 Keycloak 服务器上进行认证
-
前端用户获取 JWT 令牌。
-
在另一个时刻,前端用户使用该令牌向 Laravel API 的受保护端点发起请求。
-
Laravel API (通过
Keycloak 守卫
) 处理它。- 验证令牌签名。
- 验证令牌结构。
- 验证令牌过期时间。
- 验证我的 API 是否允许从令牌中进行
资源访问
。
-
如果一切正常,则找到数据库中的用户并在我的 API 上进行认证。
-
可选地,用户可以在 API 的用户数据库中创建/更新。
-
返回响应
安装
Laravel / Lumen
需要此包
composer require robsontenorio/laravel-keycloak-guard
Lumen 仅限
在您的 bootstrap/app.php
应用程序启动文件中注册提供者
在文件底部的 "注册服务提供者" 部分添加以下行。
$app->register(\KeycloakGuard\KeycloakGuardServiceProvider::class);
对于外观,在您的 bootstrap/app.php
应用程序启动文件中取消注释 $app->withFacades();
配置
Keycloak 守卫
⚠️ 当编辑 .env
时,请确保所有字符串 都已修剪。
# Publish config file php artisan vendor:publish --provider="KeycloakGuard\KeycloakGuardServiceProvider"
✔️ realm_public_key
必需。
Keycloak 服务器领域公钥 (字符串)。
如何获取领域公钥?点击 "领域设置" > "密钥" > "算法 RS256" 行 > "公钥" 按钮
✔️ load_user_from_database
必需。默认为 true
。
如果您没有 users
表,您必须禁用此功能。
它从数据库中检索用户并将值填充到已认证的用户对象中。如果启用,它将与 user_provider_credential
和 token_principal_attribute
一起工作。
✔️ user_provider_custom_retrieve_method
默认为 null
。
如果您有一个users
表,并希望根据令牌对其进行更新(创建或更新用户),则可以通知自定义的UserProvider上的自定义方法,该方法将代替retrieveByCredentials
被调用,并将接收完整的解码令牌作为参数,而不仅仅是凭据(默认情况)。这将允许您自定义与数据库交互的方式,在匹配并传递已验证的用户对象之前,可以访问包含在(有效)访问令牌中的所有信息。有关自定义UserProvider的更多信息,请参阅Laravel关于的文档。
显然,如果使用此功能,则定义的user_provider_credential
和token_principal_attribute
的值将被忽略。
✔️ user_provider_credential
必需。默认值为username
。
包含用户唯一标识符的字段来自"users"表(例如,用户名、电子邮件、昵称)。在认证时,将与此token_principal_attribute
属性进行比较。
✔️ token_principal_attribute
必需。默认值为preferred_username
。
包含用户标识符的JWT令牌属性。在认证时,将与此user_provider_credential
属性进行比较。
✔️ append_decoded_token
默认值为false
。
将完整的解码JWT令牌附加到已验证用户($user->token
)。如果您需要了解JWT令牌中持有的角色、组和其他用户信息,这很有用。即使选择false
,您也可以使用Auth::token()
获取它,请参阅API部分。
✔️ allowed_resources
必需.
通常您的API应该处理一个resource_access。但是,如果您处理多个,则只需使用API接受的允许资源的逗号分隔列表。在认证时,此属性将与此JWT令牌的resource_access
属性进行比较。
✔️ ignore_resources_validation
默认值为false
.
完全禁用资源验证。它将忽略allowed_resources配置。
✔️ leeway
默认值为0
.
您可以在认证服务器和验证服务器之间存在时钟偏差时添加一个宽容值。如果您遇到像"无法处理令牌在 "这样的问题,请尝试将其设置为60
(秒)。
✔️ input_key
默认为 null
。
默认情况下,此包始终会查找第一个Bearer
令牌。此外,如果启用此选项,则它将尝试从这个自定义请求参数中获取令牌。
✔️ user_service_account
默认为 null
。
当load_user_from_database = true
时,允许getEmptyModel而不在数据库中执行查询
// keycloak.php 'input_key' => 'api_token' // If there is no Bearer token on request it will use `api_token` request param GET $this->get("/foo/secret?api_token=xxxxx") POST $this->post("/foo/secret", ["api_token" => "xxxxx"])
Laravel Auth
更改config/auth.php
... 'defaults' => [ 'guard' => 'api', # <-- For sure, i`m building an API 'passwords' => 'users', ], .... 'guards' => [ # <!----- # Make sure your "api" guard looks like this. # Newer Laravel versions just removed this config block. # ----> 'api' => [ 'driver' => 'keycloak', 'provider' => 'users', ], ],
Laravel Routes
只需保护routes/api.php
中的某些端点即可完成操作!
// public endpoints Route::get('/hello', function () { return ':)'; }); // protected endpoints Route::group(['middleware' => 'auth:api'], function () { Route::get('/protected-endpoint', 'SecretController@index'); // more endpoints ... });
Lumen Routes
只需保护routes/web.php
中的某些端点即可完成操作!
// public endpoints $router->get('/hello', function () { return ':)'; }); // protected endpoints $router->group(['middleware' => 'auth'], function () { $router->get('/protected-endpoint', 'SecretController@index'); // more endpoints ... });
API
Simple Keycloak Guard实现了Illuminate\Contracts\Auth\Guard
。因此,所有Laravel默认方法都将可用。
默认Laravel方法
check()
guest()
user()
id()
validate()
setUser()
Keycloak Guard方法
令牌
token()
返回认证用户的完整解码JWT令牌。
$token = Auth::token() // or Auth::user()->token()
用户名
返回认证用户的用户名。
$token = Auth::username() // or Auth::user()->username()
角色
hasRole('some-resource', 'some-role')
检查认证用户是否在resource_access上具有角色
// Example decoded payload 'resource_access' => [ 'myapp-backend' => [ 'roles' => [ 'myapp-backend-role1', 'myapp-backend-role2' ] ], 'myapp-frontend' => [ 'roles' => [ 'myapp-frontend-role1', 'myapp-frontend-role2' ] ] ]
Auth::hasRole('myapp-backend', 'myapp-backend-role1') // true Auth::hasRole('myapp-frontend', 'myapp-frontend-role1') // true Auth::hasRole('myapp-backend', 'myapp-frontend-role1') // false
hasAnyRole('some-resource', ['some-role1', 'some-role2'])
检查认证用户是否在resource_access上具有任何角色
Auth::hasAnyRole('myapp-backend', ['myapp-backend-role1', 'myapp-backend-role3']) // true Auth::hasAnyRole('myapp-frontend', ['myapp-frontend-role1', 'myapp-frontend-role3']) // true Auth::hasAnyRole('myapp-backend', ['myapp-frontend-role1', 'myapp-frontend-role2']) // false
作用域
示例解码有效负载
{ "scope": "scope-a scope-b scope-c", }
scopes()
获取所有用户作用域
array:3 [ 0 => "scope-a" 1 => "scope-b" 2 => "scope-c" ]
hasScope('some-scope')
检查认证用户是否具有作用域
Auth::hasScope('scope-a') // true Auth::hasScope('scope-d') // false
hasAnyScope(['scope-a', 'scope-c'])
检查认证用户是否具有任何作用域
Auth::hasAnyScope(['scope-a', 'scope-c']) // true Auth::hasAnyScope(['scope-a', 'scope-d']) // true Auth::hasAnyScope(['scope-f', 'scope-k']) // false
在测试中充当Keycloak用户
与Laravel中的$this->actingAs($user)
类似的功能,使用本包,您可以在测试类中使用KeycloakGuard\ActingAsKeycloakUser
特性,然后使用actingAsKeycloakUser()
方法来模拟用户,并绕过Keycloak认证。
use KeycloakGuard\ActingAsKeycloakUser; public test_a_protected_route() { $this->actingAsKeycloakUser() ->getJson('/api/somewhere') ->assertOk(); }
如果您没有使用keycloak.load_user_from_database
选项,请在测试时设置keycloak.preferred_username
,并使用有效的preferred_username
。
贡献
您可以使用VSCODE的远程容器来运行此项目。请确保您使用的是内部VSCODE终端(在运行容器内部)。
composer install
composer test
composer test:coverage
联系
Twitter @robsontenorio