api-skeletons / laravel-doctrine-apikey
Laravel Doctrine 的 API 密钥,具有作用域
Requires
- php: ^8.1
- api-skeletons/laravel-api-problem: ^2.0
- laravel-doctrine/orm: ^2.0 || ^3.0
Requires (Dev)
- doctrine/annotations: ^2.0
- doctrine/coding-standard: ^12.0
- doctrine/dbal: ^3.8
- orchestra/testbench: ^7.41
- php-parallel-lint/php-parallel-lint: ^1.4
- phpunit/phpunit: ^9.5
- vimeo/psalm: ^4.15
README
此仓库提供 Doctrine 的驱动程序,可以添加到现有的实体管理器中。
该驱动程序提供一组实体,通过 HTTP 中间件实现 ApiKey 授权。支持作用域!这是其他仓库中缺少的组件,也是创建此库的催化剂。
安装
运行以下命令使用 Composer 安装此库
composer require api-skeletons/laravel-doctrine-apikey
快速入门
将服务提供者添加到 app.php
'providers' => [ ... ApiSkeletons\Laravel\Doctrine\ApiKey\ServiceProvider::class, ],
将路由中间件添加到 Http Kernel
use ApiSkeletons\Laravel\Doctrine\ApiKey\Http\Middleware\AuthorizeApiKey; $routeMiddleware = [ ... 'auth.apikey' => AuthorizeApiKey:class ];
在 App\Providers\AppServiceProvider
中初始化实体管理器的 ApiKey 服务
use ApiSkeletons\Laravel\Doctrine\ApiKey\Service\ApiKeyService; public function boot() { app(ApiKeyService::class)->init(app('em')); }
通过控制台添加 API 密钥
$ php artisan apikey:generate yourapikeyname
将中间件添加到受保护的路由
Route::name('api.resource::fetch') ->get('resource', 'ResourceController::fetch') ->middleware('auth.apikey');
开始通过在 Authorization 标头的 Bearer token 中使用 apikey 向受保护的资源发送请求
Authorization: Bearer {apikey}
架构
使用作用域
作用域是 ApiKey 的权限。它们在 OAuth2 中很常见,但在 ApiKey 中不太常见。创建一个作用域
php artisan apikey:scope:generate {name}
使用作用域进行的安全措施与用于验证 ApiKey 的相同中间件应用。将 {scopeName} 替换为您的范围名称,中间件将确保传递的 ApiKey 具有该范围以继续。
Route::name('api.resource::fetch') ->get('resource', 'ResourceController::fetch') ->middleware('auth.apikey:{scopeName}');
通过请求属性访问 ApiKey
用于验证请求的 ApiKey 实体被分配到请求属性作为 'apikey'。
$apiKey = request()->attributes->get('apikey');
使用外键到 ApiKey
由于 ApiKey 可以重新生成,因此没有必要将多个 API 密钥分配给同一实体。例如,如果每个客户都有一对一的 ApiKey,那么您可以安全地禁用该密钥,重新生成它,等等;永远不需要分配新的 ApiKey。
为了在客户实体和 API 密钥之间动态创建一对一关系,请创建一个事件订阅者
<?php declare(strict_types=1); namespace App\ORM\Event\Subscriber; use ApiSkeletons\Laravel\Doctrine\ApiKey\Entity\ApiKey; use App\ORM\Entity\Customer; use Doctrine\Common\EventSubscriber; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; use Doctrine\ORM\Events; class ApiKeyEventSubscriber implements EventSubscriber { /** * {@inheritDoc} */ public function getSubscribedEvents() { return [ Events::loadClassMetadata, ]; } public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs): void { // the $metadata is the whole mapping info for this class $metadata = $eventArgs->getClassMetadata(); switch ($metadata->getName()) { case Customer::class: $metadata->mapOneToOne([ 'targetEntity' => ApiKey::class, 'fieldName' => 'apiKey', ]); break; default: break; } } }
事件日志
当 ApiKey 生成、激活、禁用、添加作用域和删除作用域时,将记录管理事件。
当路由中间件允许访问资源时,将记录访问事件。
命令
API 密钥的管理通过命令行处理。但是,可以通过 Doctrine 存储库(ApiKeyRepository 和 ScopeRepository)完全访问所有数据创建函数。
生成 ApiKey
php artisan apikey:generate {name}
生成作用域
php artisan apikey:scope:generate {name}
将作用域分配给 ApiKey
php artisan apikey:scope:add {apiKeyName} {scopeName}
禁用 ApiKey
php artisan apikey:deactivate {name}
激活 ApiKey
php artisan apikey:activate {name}
从 ApiKey 中取消分配作用域
php artisan apikey:scope:remove {apiKeyName} {scopeName}
重新生成 ApiKey(分配新的 Bearer token)
php artisan apikey:regenerate {name}
删除作用域
php artisan apikey:scope:delete {scopeName}
打印 ApiKey[s]
php artisan apikey:print {name?}
打印 Scope[s]
php artisan apikey:scope:print {name?}
多个对象管理器
此仓库包含的元数据可以在多个对象管理器中正常工作。
此仓库包含的命令仅在默认 ApiKeyService 上工作,因此您需要第二种方法来维护第二个对象管理器中的数据。为了使用多个对象管理器,您必须进行一些配置。假设您已经遵循了上面的快速入门步骤,请按照以下步骤进行第二个对象管理器的配置
在 App\Providers\AppServiceProvider
中创建 ApiKeyService 的一个新单例,并使用不同的名称
use ApiSkeletons\Laravel\Doctrine\ApiKey\Service\ApiKeyService; public function register(): void { $this->app->singleton('ApiKeyService2', static function ($app) { return new ApiKeyService(); }); }
在 App\Providers\AppServiceProvider
中为第二个实体管理器初始化 ApiKey 服务
public function boot() { app('ApiKeyService2')->init(app('em2')); }
将路由中间件复制到一个新类,并使用依赖注入为 ApiKeyService2
使用
$routeMiddleware = [ ... 'auth.apikey2' => EditedAuthorizeApiKey:class ];
灵感来源于
这个仓库的灵感来源于https://github.com/ejarnutowski/laravel-api-key。它是一个不错的项目,但没有单元测试或作用域。