malvik-lab/laravel-jwt

Laravel JSON Web Token 认证

1.0.16 2024-05-12 18:27 UTC

This package is auto-updated.

Last update: 2024-09-12 19:15:29 UTC


README

Laravel JWT 是一个开源库,为 Laravel 项目添加 JWT 认证功能。
JWT 是一种基于令牌的认证方法,它安全、轻量级且易于实现。该库易于使用,几分钟内即可配置。它提供了 JWT 认证的一系列功能,包括

  • 生成和验证 JWT 令牌
  • 管理用户和令牌
  • 保护路由和控制器

预安装(非必需)

JWT 使用一对公钥和私钥来签名和验证令牌。公钥用于客户端验证令牌,私钥用于服务器端签名令牌。

默认情况下,该库将公钥和私钥存储在项目根目录下名为 keys 的文件夹中。您可以通过向 .env 文件中添加以下内容来更改此文件夹的位置

JWT_KEYS_DIRECTORY_PATH=/path/to/jwt/keys/directory

库安装

此命令将安装库及其所有依赖项。

composer require malvik-lab/laravel-jwt

发布配置

此文件包含库配置,例如令牌签名方法、令牌有效期限和 JWT 密钥路径。

php artisan vendor:publish --tag=jwt-config

迁移发布

此文件将在您的数据库中创建 jwt_tokens 表。此表用于存储发行的 JWT 令牌。

php artisan vendor:publish --tag=jwt-migration

运行迁移

此命令将在您的数据库中创建 jwt_tokens 表。

php artisan migrate

JWT 密钥生成

此命令将生成一对公钥和私钥,这些密钥将用于签名和验证 JWT 令牌。

php artisan jwt:keys

添加守卫

向 auth 配置文件中添加新的守卫。

<?php
## config/auth.php

return [
    // ...

    'guards' => [
        // ...
        
        'jwt-access-token' => [
            'driver' => 'jwt-access-token',
            'provider' => 'users',
        ],
    
        'jwt-refresh-token' => [
            'driver' => 'jwt-refresh-token',
            'provider' => 'users',
        ]
    ],

    // ...
];

启用 JWT API 认证

<?php
## app/Providers/AuthServiceProvider.php

namespace App\Providers;

// ...
use Illuminate\Support\Facades\Auth;
use Illuminate\Contracts\Foundation\Application;
use MalvikLab\LaravelJwt\Http\Guards\JwtAccessTokenGuard;
use MalvikLab\LaravelJwt\Http\Guards\JwtRefreshTokenGuard;

class AuthServiceProvider extends ServiceProvider
{
    protected $policies = [
        // ...
    ];

    public function boot(): void
    {
        // ...
    
        Auth::extend('jwt-access-token', function (Application $app, string $name, array $config) {
            return new JwtAccessTokenGuard(Auth::createUserProvider($config['provider']));
        });

        Auth::extend('jwt-refresh-token', function (Application $app, string $name, array $config) {
            return new JwtRefreshTokenGuard(Auth::createUserProvider($config['provider']));
        });
    }
}

路由

为了测试 JWT 认证,您可以在项目中添加允许使用有效的 JWT 令牌进行认证的路由。

<?php
## app/routes/api.php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use MalvikLab\LaravelJwt\Http\Controllers\AuthController;

Route::middleware(['guest'])->group(function () {
    Route::post('/auth/login', [AuthController::class, 'login']);
});

Route::middleware(['auth:jwt-access-token'])->group(function () {
    Route::post('/auth/logout', [AuthController::class, 'logout']);
    Route::get('/auth/me', [AuthController::class, 'me']);
});

Route::middleware(['auth:jwt-refresh-token'])->group(function () {
    Route::post('/auth/refresh', [AuthController::class, 'refresh']);
});

控制器

对于基本使用,您可以使用 MalvikLab\LaravelJwt\Http\Controllers\AuthController(如前例所示),或使用自定义控制器以获得对认证逻辑的更大控制。

<?php

namespace App\Http\Controllers;

use Illuminate\Contracts\Auth\Guard;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use MalvikLab\LaravelJwt\Services\AuthService\AuthService;
use MalvikLab\LaravelJwt\Services\JwtService\TokenOptions;

class AuthController extends Controller
{
    private AuthService $authService;
    private Guard $accessTokenGuard;

    public function __construct()
    {
        $this->authService = new AuthService();
        $this->accessTokenGuard = Auth::guard('jwt-access-token');
    }

    public function login(Request $request): JsonResponse
    {
        // Validate the request and retrieve the user
        // or use the Auth Service method
        $user = $this->authService->checkCredentials($request->all());

        $options = new TokenOptions();
        $options->setRole('mod');
        $options->setPermissions([
            'add-post',
            'edit-post',
            'delete-post'
        ]);
        $options->setAccessTokenTtl(14400);
        $options->setRefreshTokenTtl(2592000);

        $this->accessTokenGuard->login($user, $options);

        return $this->accessTokenGuard->response();
    }
    
    public function me(Request $request): JsonResponse
    {
        $authToken = $this->accessTokenGuard->getAuthToken();

        $this->accessTokenGuard->hasRole('mod');
        $this->accessTokenGuard->hasRoles(['mod', 'other-role']);
        $this->accessTokenGuard->hasPermission('add-post');
        $this->accessTokenGuard->hasPermissions(['add-post', 'edit-post', 'delete-post']);

        return response()->json($request->user());
    }
}

推荐但非必需

为了确保始终返回 JSON 响应,建议执行以下操作

添加渲染方法

<?php
## app/Exceptions/Handler.php

namespace App\Exceptions;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Response;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
use Throwable;

class Handler extends ExceptionHandler
{
    protected $dontFlash = [
        // ...
    ];

    public function register(): void
    {
        $this->reportable(function (Throwable $e) {
            //
        });
    }

    public function render($request, Throwable $e): Response | JsonResponse | RedirectResponse |SymfonyResponse
    {
        if ( $request->is('api/*') )
        {
            $request->headers->set('accept', 'application/json');
        }

        return parent::render($request, $e);
    }
}

添加 AcceptJson 中间件

<?php
## app/Http/Kernel.php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    protected $middleware = [
        // ...
    ];

    protected $middlewareGroups = [
        'web' => [
            // ...
        ],

        'api' => [
            \MalvikLab\LaravelJwt\Http\Middleware\AcceptJson::class,
            // ...
        ],
    ];

    // ...
}

安装完成

现在您已安装 Laravel JWT 库,可以开始使用其功能。以下是您可以使用来测试 JWT 认证的一些请求和响应示例。

登录

请求

POST /api/auth/login HTTP/1.1

{
    "email": "john.doe@example.com",
    "password": "123456789"
}

响应

HTTP/1.1 200 OK
Content-Type: application/json

{
    "token_type": "Bearer",
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0Iiwic3ViIjoiOWEzZTI5NDktYzcwZS00ZmFmLWEzMDEtZWQ3MmVhOGM5NzM5IiwidG9rZW5fdHlwZSI6IkFDQ0VTU19UT0tFTiIsImlhdCI6MTY5NjA2ODE2OCwianRpIjoiMzc4OWFlZmUtMWUxZC00ODI3LWFjZjEtNWY1MWJkYTAzMWY5IiwiZXhwIjoxNjk2MDgyNTY4LCJ1c2VyIjp7ImlkIjoiOWEzZTI5NDktYzcwZS00ZmFmLWEzMDEtZWQ3MmVhOGM5NzM5IiwibmFtZSI6IkpvaG4gRG9lIiwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSJ9fQ.fhwqM01o6ieNoegkczJGdlB5xEcLyD6ZHWu-0avS7WZhUp5iUQLdF6_qMKpWvgiuiPYoPtxrAowG3SIbYakjYSr1pdnBrN9Pg2T4ONTqYO0VQiVCEYujvN-XHKcsG4xvkkdVxe2v75_nFPxnWxVFgg3xQZFmjuoUtpFWHf5TQSjIebDxMwO1wDseohI-8GlP69rGR-8KIWh9Ig_fPRz_Hsrjognhi8Q6vZpW4w3e0uW2xyCa4gpF-JfKHvR1qXRFyaZFD2MuP614U740Xk3Gbqc0YTzbNzYBWbivtcjZs8d6QobBE1-KJjqoDtHQ3RV9nuV0SSGvrABxpW4dFOcxWg",
    "refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0Iiwic3ViIjoiOWEzZTI5NDktYzcwZS00ZmFmLWEzMDEtZWQ3MmVhOGM5NzM5IiwidG9rZW5fdHlwZSI6IlJFRlJFU0hfVE9LRU4iLCJpYXQiOjE2OTYwNjgxNjgsImp0aSI6IjRhNTBlOWE5LWQ2MzktNGZmNy05NWI2LTA2NzAxYjUxMjMzZCIsImV4cCI6MTY5ODY2MDE2OH0.A-keVY5H-smKTRGVlVWLosmftK4f-927tfPVKxALznumQN4L2Q3xN4bmi-6mkHp8XBZZvtKLdAXZzWCMRwz4_EvGg02lNbGnZoI7qQ-scHGu7zDc3Bbs-_FXCCYchrSijo-rtjAlAoD1c6ilr9VYXjOq6-QBAJvx10v-IjGZRcsXiveef7XFYwEz9rb605lQdvpNLOoulGT3R43BXlszbfObqWBz-WObBlL-AVPleiiHAbDNLRdisJhv-XyV1G4YKY_SLEMv7u8q09t5J6L1aj3Reya3bSm4JEJP6-3WZIUzIopaVhSVJd4SjHjr0F4iSDR73rQYtdaWYYtHRs3-YQ",
    "expire_in": 14400
}

请求

GET /api/auth/me HTTP/1.1

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0Iiwic3ViIjoiOWEzZTI5NDktYzcwZS00ZmFmLWEzMDEtZWQ3MmVhOGM5NzM5IiwidG9rZW5fdHlwZSI6IkFDQ0VTU19UT0tFTiIsImlhdCI6MTY5NjA2ODE2OCwianRpIjoiMzc4OWFlZmUtMWUxZC00ODI3LWFjZjEtNWY1MWJkYTAzMWY5IiwiZXhwIjoxNjk2MDgyNTY4LCJ1c2VyIjp7ImlkIjoiOWEzZTI5NDktYzcwZS00ZmFmLWEzMDEtZWQ3MmVhOGM5NzM5IiwibmFtZSI6IkpvaG4gRG9lIiwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSJ9fQ.fhwqM01o6ieNoegkczJGdlB5xEcLyD6ZHWu-0avS7WZhUp5iUQLdF6_qMKpWvgiuiPYoPtxrAowG3SIbYakjYSr1pdnBrN9Pg2T4ONTqYO0VQiVCEYujvN-XHKcsG4xvkkdVxe2v75_nFPxnWxVFgg3xQZFmjuoUtpFWHf5TQSjIebDxMwO1wDseohI-8GlP69rGR-8KIWh9Ig_fPRz_Hsrjognhi8Q6vZpW4w3e0uW2xyCa4gpF-JfKHvR1qXRFyaZFD2MuP614U740Xk3Gbqc0YTzbNzYBWbivtcjZs8d6QobBE1-KJjqoDtHQ3RV9nuV0SSGvrABxpW4dFOcxWg

响应

HTTP/1.1 200 OK
Content-Type: application/json

{
    "id": "9a3e2949-c70e-4faf-a301-ed72ea8c9739",
    "name": "John Doe",
    "email": "john.doe@example.com"
}

刷新令牌

请求

POST /api/auth/refresh HTTP/1.1

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0Iiwic3ViIjoiOWEzZTI5NDktYzcwZS00ZmFmLWEzMDEtZWQ3MmVhOGM5NzM5IiwidG9rZW5fdHlwZSI6IlJFRlJFU0hfVE9LRU4iLCJpYXQiOjE2OTYwNjgxNjgsImp0aSI6IjRhNTBlOWE5LWQ2MzktNGZmNy05NWI2LTA2NzAxYjUxMjMzZCIsImV4cCI6MTY5ODY2MDE2OH0.A-keVY5H-smKTRGVlVWLosmftK4f-927tfPVKxALznumQN4L2Q3xN4bmi-6mkHp8XBZZvtKLdAXZzWCMRwz4_EvGg02lNbGnZoI7qQ-scHGu7zDc3Bbs-_FXCCYchrSijo-rtjAlAoD1c6ilr9VYXjOq6-QBAJvx10v-IjGZRcsXiveef7XFYwEz9rb605lQdvpNLOoulGT3R43BXlszbfObqWBz-WObBlL-AVPleiiHAbDNLRdisJhv-XyV1G4YKY_SLEMv7u8q09t5J6L1aj3Reya3bSm4JEJP6-3WZIUzIopaVhSVJd4SjHjr0F4iSDR73rQYtdaWYYtHRs3-YQ

响应(与登录响应相同)

登出

请求

POST /api/auth/logout HTTP/1.1

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0Iiwic3ViIjoiOWEzZTI5NDktYzcwZS00ZmFmLWEzMDEtZWQ3MmVhOGM5NzM5IiwidG9rZW5fdHlwZSI6IkFDQ0VTU19UT0tFTiIsImlhdCI6MTY5NjA2ODE2OCwianRpIjoiMzc4OWFlZmUtMWUxZC00ODI3LWFjZjEtNWY1MWJkYTAzMWY5IiwiZXhwIjoxNjk2MDgyNTY4LCJ1c2VyIjp7ImlkIjoiOWEzZTI5NDktYzcwZS00ZmFmLWEzMDEtZWQ3MmVhOGM5NzM5IiwibmFtZSI6IkpvaG4gRG9lIiwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSJ9fQ.fhwqM01o6ieNoegkczJGdlB5xEcLyD6ZHWu-0avS7WZhUp5iUQLdF6_qMKpWvgiuiPYoPtxrAowG3SIbYakjYSr1pdnBrN9Pg2T4ONTqYO0VQiVCEYujvN-XHKcsG4xvkkdVxe2v75_nFPxnWxVFgg3xQZFmjuoUtpFWHf5TQSjIebDxMwO1wDseohI-8GlP69rGR-8KIWh9Ig_fPRz_Hsrjognhi8Q6vZpW4w3e0uW2xyCa4gpF-JfKHvR1qXRFyaZFD2MuP614U740Xk3Gbqc0YTzbNzYBWbivtcjZs8d6QobBE1-KJjqoDtHQ3RV9nuV0SSGvrABxpW4dFOcxWg

响应

HTTP/1.1 204 No Content