Работа с JWT на Laravel 8

29 Января 2022 03:17

Ссылка на офф. сайт - https://jwt-auth.readthedocs.io/en/develop/laravel-installation/

Установка

composer require tymon/jwt-auth

Добавление в конфиг

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

Генерация ключа

php artisan jwt:secret

Удостоверямся, что ключ был добавлен в .env файл.

Расширение модели User

Поскольку, такая вещь как авторизация напрямую связана с пользователем то необходимо настроить модель User на работу с JWT токенами.

Идем в App/Model/User.php. Приводим модель к следующему виду:

<?php

namespace App;

use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements JWTSubject
{
    use Notifiable;

    // Rest omitted for brevity

    /**
     * Get the identifier that will be stored in the subject claim of the JWT.
     *
     * @return mixed
     */
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    /**
     * Return a key value array, containing any custom claims to be added to the JWT.
     *
     * @return array
     */
    public function getJWTCustomClaims()
    {
        return [];
    }
}

Ключевое здесь:

  • Подключаем JWTSubject;
  • Имплементируем JWTSubject - implements JWTSubject;
  • Добавляем два метода:
    1. getJWTIdentifier();
    2. getJWTCustomClaims().

Установка Auth Guard

Идем в config/auth.php.

В разделе Authentication Defaults в ключ guard прописываем - api.

    /*
    |--------------------------------------------------------------------------
    | Authentication Defaults
    |--------------------------------------------------------------------------
    |
    | This option controls the default authentication "guard" and password
    | reset options for your application. You may change these defaults
    | as required, but they're a perfect start for most applications.
    |
    */

    'defaults' => [
        'guard' => 'api',
        'passwords' => 'users',
    ],

В разделе Authentication Guards добавлем этот guard

    /*
    |--------------------------------------------------------------------------
    | Authentication Guards
    |--------------------------------------------------------------------------
    |
    | Next, you may define every authentication guard for your application.
    | Of course, a great default configuration has been defined for you
    | here which uses session storage and the Eloquent user provider.
    |
    | All authentication drivers have a user provider. This defines how the
    | users are actually retrieved out of your database or other storage
    | mechanisms used by this application to persist your user's data.
    |
    | Supported: "session"
    |
    */

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver'   => 'jwt',
            'provider' => 'users'
        ]
    ],

Добавление маршрутов

Идем в routes/api.php.

Добавляем группу маршрутов

Route::prefix('auth')->middleware('api')->group(function ($router) {
    Route::post('login', [AuthController::class, 'login']);
    Route::post('logout', [AuthController::class, 'logout']);
    Route::post('refresh', [AuthController::class, 'refresh']);
    Route::post('me', [AuthController::class, 'me']);
});

Описание маршрутов:

  1. login - формирует Bearer Token на основе данных email и password
  2. logout - удаляет токен из системы, производит выход
  3. refresh - формирует новый токен на основе старого. У токена есть время действия (по умолчанию, 3600 секунд), после его истечения необходимо побновлять токен.
  4. me - возвращает JSON с данными текущего пользователя, которые были указаны в модели

На этом этапе рекомендуется размещать маршруты с данными внутри маршрута auth с использованием миддлвейра jwt.auth. Например:

Route::prefix('auth')->middleware('api')->group(function ($router) {
    Route::post('login', [AuthController::class, 'login']);
    Route::post('logout', [AuthController::class, 'logout']);
    Route::post('refresh', [AuthController::class, 'refresh']);
    Route::post('me', [AuthController::class, 'me']);
    
    Route::middleware('jwt.auth')->group(function() {
        Route::prefix('tasks')->group(function() {
            Route::get('/', [TaskListController::class, 'index']);
            Route::post('list/store', [TaskListController::class, 'store']);
        });
    });
});

Примечание. jwt.auth - этот миддлвейр находится внутри компонента Tymon и грузится прямо оттуда. В документации Tymon этого не указано, но лучше всего использовать именно этот миддлвейр.

Создание контроллера AuthController

Прописываем команду в терминал

php artisan make:controller AuthController

Во вновьсозданный контроллер(app/Http/controllers/AuthController.php) добавляем содержимое:

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;

class AuthController extends Controller
{
    /**
     * Create a new AuthController instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth:api', ['except' => ['login']]);
    }

    /**
     * Get a JWT via given credentials.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function login()
    {
        $credentials = request(['email', 'password']);

        if (! $token = auth()->attempt($credentials)) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        return $this->respondWithToken($token);
    }

    /**
     * Get the authenticated User.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function me()
    {
        return response()->json(auth()->user());
    }

    /**
     * Log the user out (Invalidate the token).
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout()
    {
        auth()->logout();

        return response()->json(['message' => 'Successfully logged out']);
    }

    /**
     * Refresh a token.
     *
     * @return \Illuminate\Http\JsonResponse
     */
    public function refresh()
    {
        return $this->respondWithToken(auth()->refresh());
    }

    /**
     * Get the token array structure.
     *
     * @param  string $token
     *
     * @return \Illuminate\Http\JsonResponse
     */
    protected function respondWithToken($token)
    {
        return response()->json([
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_in' => auth()->factory()->getTTL() * 60
        ]);
    }
}

На этом этапе, при отправке запроса с email и password на адрес htpp://localhost:8080/auth/login будет возвращен ответ с токеном такого вида:

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ",
    "token_type": "bearer",
    "expires_in": 3600
}

На текущем этапе, настройка JWT токена на бэке завершена.

Конфиг

Файл - config/jwt.php.

  1.  'ttl' => env('JWT_TTL', 60) - Регулировка времени истечения токена
  2. 'refresh_ttl' => env('JWT_REFRESH_TTL', 20160) - Регулирвока времени на обновленный токен. На него обычно выделяется больше времени.