bombenprodukt/laravel-zeus

一个支持前缀和权限范围支持的Laravel兼容API令牌管理器。

1.0.1 2023-07-27 08:30 UTC

README

该项目由Brian Faust创建并维护,是一个支持前缀和权限范围支持的Laravel兼容API令牌管理器。请确保浏览变更日志行为准则贡献指南许可协议安全策略

设计

在设计目标上,Laravel Zeus类似于Laravel Sanctum。它提供了一个轻量级的API认证系统,类似于Sanctum。然而,重要的是要注意,Zeus并不是用来替代Sanctum、Passport或Fortify的。相反,它为那些不需要上述系统丰富功能的开发者提供了一个更简单的替代方案。它的主要功能是使用易于识别的API令牌对用户进行认证。这种设计选择使得检测和吊销令牌变得更容易。

所有访问令牌都存储在access_tokens表中,并在生成时添加前缀。前缀有助于区分令牌类型,例如,pat用于个人访问令牌。我们使用TypeID生成这些令牌。它创建类型安全的、K排序的、全局唯一的API密钥。该设计灵感来源于Stripe IDs所使用的系统。

此外,Laravel Zeus还提供了将令牌限制为特定能力和域的选项。这一措施增加了对令牌使用的另一层控制。令牌还可以被编程在创建后指定时间内过期。此功能特别适用于创建一次性使用场景的短期令牌或信任应用的长期令牌。此自动过期功能不仅增强了安全性,还强制进行定期令牌轮换,减轻了永久令牌泄露的风险。

安装

注意 此软件包需要PHP 8.2或更高版本,并支持Laravel 10或更高版本。

要获取最新版本,只需使用Composer安装项目。

$ composer require bombenprodukt/laravel-zeus

您可以使用以下命令发布迁移:

$ php artisan vendor:publish --tag="laravel-zeus-migrations"

您可以使用以下命令发布配置文件:

$ php artisan vendor:publish --tag="laravel-zeus-config"

使用方法

注意 请查看我们的测试套件以获取详细的使用示例。

开始的第一步是准备您的模型,通过实现HasAccessTokensInterface接口。这可以通过包含提供所有必要方法默认实现的HasAccessTokens特性来完成。过程如下:

<?php

namespace App\Models;

+ use BombenProdukt\Zeus\HasAccessTokens;
+ use BombenProdukt\Zeus\HasAccessTokensInterface;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Jetstream\HasProfilePhoto;
use Laravel\Jetstream\HasTeams;
- use Laravel\Sanctum\HasApiTokens;

- class User extends Authenticatable implements MustVerifyEmail
+ class User extends Authenticatable implements HasAccessTokensInterface, MustVerifyEmail
{
-    use HasApiTokens;
+    use HasAccessTokens;
    use HasFactory;
    use HasProfilePhoto;
    use HasTeams;
    use Notifiable;
    use TwoFactorAuthenticatable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
        'two_factor_recovery_codes',
        'two_factor_secret',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    /**
     * The accessors to append to the model's array form.
     *
     * @var array<int, string>
     */
    protected $appends = [
        'profile_photo_url',
    ];
}

完成之后,您可以打开您的 routes/api.php 文件,将 auth:sanctum 替换为 auth:zeus。只要使用有效的 Zeus 访问令牌,一切应该都能像以前一样工作。

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "api" middleware group. Make something great!
|
*/

-Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
+Route::middleware('auth:zeus')->get('/user', function (Request $request) {
    return $request->user();
});

现在您已经设置了模型,您可以创建一个控制器来存储基于用户输入的访问令牌,例如为其提供一个名称。查看 PendingAccessToken 类以获取所有可用的获取器和设置器。然而,在大多数情况下,一旦您已将其配置为匹配您的用例,坚持默认设置就足够了。

<?php

namespace App\Http\Controllers;

use App\Models\User;
use BombenProdukt\Zeus\CommitAccessToken;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

class StoreAccessTokenController
{
    public function __invoke(Request $request, CommitAccessToken $commitAccessToken): Response
    {
        return $commitAccessToken(
            PendingAccessToken::defaults()->setName($request->input('name')),
            $request->user()
        );
    }
}

如果您想根据访问令牌而不是用户ID或IP地址应用速率限制,您可以通过修改 app/Providers/RouteServiceProvider.php 文件中的API速率限制器,并将访问令牌指定为速率限制尝试的标识符来实现这一点。

<?php

namespace App\Providers;

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;

class RouteServiceProvider extends ServiceProvider
{
    /**
     * The path to your application's "home" route.
     *
     * Typically, users are redirected here after authentication.
     *
     * @var string
     */
    public const HOME = '/dashboard';

    /**
     * Define your route model bindings, pattern filters, and other route configuration.
     */
    public function boot(): void
    {
        RateLimiter::for('api', function (Request $request) {
-            return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
+            return Limit::perMinute(60)->by($request->user()?->getAccessToken()?->token ?: $request->ip());
        });

        $this->routes(function () {
            Route::middleware('api')
                ->prefix('api')
                ->group(base_path('routes/api.php'));

            Route::middleware('web')
                ->group(base_path('routes/web.php'));
        });
    }
}