laravel-12/SANCTUM_SETUP_GUIDE.md

23 KiB
Raw Blame History

Руководство по установке и настройке Laravel Sanctum с API авторизацией

Описание

Данное руководство описывает процесс установки и настройки Laravel Sanctum для API аутентификации с использованием wadakatu/laravel-spectrum для генерации Swagger документации.


Содержание

  1. Установка пакетов
  2. Настройка Sanctum
  3. Структура API
  4. API Endpoints
  5. Использование API
  6. Swagger документация
  7. Команды Artisan

1. Установка пакетов

1.1 Установка Laravel Sanctum

composer require laravel/sanctum

1.2 Установка wadakatu/laravel-spectrum

composer require wadakatu/laravel-spectrum

1.3 Публикация конфигураций

# Публикация Sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"

# Публикация Spectrum
php artisan vendor:publish --provider="Wadakatu\LaravelSpectrum\LaravelSpectrumServiceProvider"

1.4 Запуск миграций

php artisan migrate

Примечание: Если таблица personal_access_tokens уже существует, пропустите этот шаг.


2. Настройка Sanctum

2.1 Обновление модели User

Добавьте трейт HasApiTokens в модель User:

// app/Models/User.php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    // ...
}

2.2 Настройка middleware

Обновите файл bootstrap/app.php:

// bootstrap/app.php

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware): void {
        $middleware->statefulApi();
    })
    ->withExceptions(function (Exceptions $exceptions): void {
        //
    })->create();

2.3 Конфигурация Sanctum

Конфигурация находится в файле config/sanctum.php. Основные настройки:

// config/sanctum.php

return [
    'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
        '%s%s',
        'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
        env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : ''
    ))),

    'guard' => ['web'],

    'expiration' => null,

    'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''),

    'middleware' => [
        'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
        'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class,
    ],
];

3. Структура API

3.1 Директории

app/
├── Http/
│   ├── Controllers/
│   │   └── Api/
│   │       ├── AuthController.php
│   │       └── PostController.php
│   ├── Requests/
│   │   ├── LoginRequest.php
│   │   ├── RegisterRequest.php
│   │   ├── StorePostRequest.php
│   │   └── UpdatePostRequest.php
│   └── Resources/
│       ├── PostCollection.php
│       ├── PostResource.php
│       └── UserResource.php
└── Policies/
    └── PostPolicy.php

3.2 API Resources

UserResource - форматирование данных пользователя:

// app/Http/Resources/UserResource.php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'role' => $this->role,
            'created_at' => $this->created_at,
        ];
    }
}

PostResource - форматирование данных поста:

// app/Http/Resources/PostResource.php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class PostResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'content' => $this->content,
            'user_id' => $this->user_id,
            'user' => new UserResource($this->whenLoaded('user')),
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
        ];
    }
}

3.3 Form Requests

RegisterRequest - валидация регистрации:

// app/Http/Requests/RegisterRequest.php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class RegisterRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
        ];
    }
}

LoginRequest - валидация входа:

// app/Http/Requests/LoginRequest.php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class LoginRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'email' => ['required', 'string', 'email'],
            'password' => ['required', 'string'],
        ];
    }
}

StorePostRequest - валидация создания поста:

// app/Http/Requests/StorePostRequest.php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StorePostRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'title' => ['required', 'string', 'max:255'],
            'content' => ['required', 'string'],
        ];
    }
}

UpdatePostRequest - валидация обновления поста:

// app/Http/Requests/UpdatePostRequest.php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class UpdatePostRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'title' => ['sometimes', 'string', 'max:255'],
            'content' => ['sometimes', 'string'],
        ];
    }
}

4. API Endpoints

4.1 Маршруты

// routes/api.php

use App\Http\Controllers\Api\AuthController;
use App\Http\Controllers\Api\PostController;
use Illuminate\Support\Facades\Route;

// Swagger Documentation
Route::get('/docs', function () {
    return redirect()->to('/docs/index.html');
});

// Public routes
Route::post('/register', [AuthController::class, 'register'])->name('api.register');
Route::post('/login', [AuthController::class, 'login'])->name('api.login');

// Protected routes
Route::middleware('auth:sanctum')->group(function () {
    // Auth
    Route::post('/logout', [AuthController::class, 'logout'])->name('api.logout');
    Route::get('/user', [AuthController::class, 'user'])->name('api.user');

    // Posts
    Route::get('/posts', [PostController::class, 'index'])->name('api.posts.index');
    Route::post('/posts', [PostController::class, 'store'])->name('api.posts.store');
    Route::get('/posts/{post}', [PostController::class, 'show'])->name('api.posts.show');
    Route::put('/posts/{post}', [PostController::class, 'update'])->name('api.posts.update');
    Route::delete('/posts/{post}', [PostController::class, 'destroy'])->name('api.posts.destroy');
});

4.2 Список Endpoints

Метод Endpoint Описание Авторизация
POST /api/register Регистрация Нет
POST /api/login Вход Нет
POST /api/logout Выход Да
GET /api/user Текущий пользователь Да
GET /api/posts Список постов Да
POST /api/posts Создание поста Да
GET /api/posts/{id} Просмотр поста Да
PUT /api/posts/{id} Обновление поста Да
DELETE /api/posts/{id} Удаление поста Да
GET /api/docs Swagger UI Нет

5. Использование API

5.1 Регистрация

Запрос:

curl -X POST http://la.test/api/register \
  -H "Content-Type: application/json" \
  -d '{
    "name": "John Doe",
    "email": "john@example.com",
    "password": "password123",
    "password_confirmation": "password123"
  }'

Ответ:

{
  "message": "User registered successfully",
  "user": {
    "id": 1,
    "name": "John Doe",
    "email": "john@example.com",
    "role": null,
    "created_at": "2026-03-20T03:45:00.000000Z"
  },
  "token": "1|abc123..."
}

5.2 Вход

Запрос:

curl -X POST http://la.test/api/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "john@example.com",
    "password": "password123"
  }'

Ответ:

{
  "message": "Login successful",
  "user": {
    "id": 1,
    "name": "John Doe",
    "email": "john@example.com",
    "role": null,
    "created_at": "2026-03-20T03:45:00.000000Z"
  },
  "token": "2|def456..."
}

5.3 Создание поста

Запрос:

curl -X POST http://la.test/api/posts \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer {token}" \
  -d '{
    "title": "My First Post",
    "content": "This is the content of my post."
  }'

Ответ:

{
  "message": "Post created successfully",
  "post": {
    "id": 1,
    "title": "My First Post",
    "content": "This is the content of my post.",
    "user_id": 1,
    "user": {
      "id": 1,
      "name": "John Doe",
      "email": "john@example.com"
    },
    "created_at": "2026-03-20T03:50:00.000000Z",
    "updated_at": "2026-03-20T03:50:00.000000Z"
  }
}

5.4 Обновление поста

Запрос:

curl -X PUT http://la.test/api/posts/1 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer {token}" \
  -d '{
    "title": "Updated Title"
  }'

5.5 Удаление поста

Запрос:

curl -X DELETE http://la.test/api/posts/1 \
  -H "Authorization: Bearer {token}"

5.6 Выход

Запрос:

curl -X POST http://la.test/api/logout \
  -H "Authorization: Bearer {token}"

6. Swagger документация

6.1 Доступ к документации

Swagger UI доступен по адресу: {APP_URL}/api/docs

Например: http://la.test/api/docs

6.2 Структура файлов документации

public/
└── docs/
    ├── index.html      # Swagger UI
    └── openapi.yaml    # OpenAPI спецификация

6.3 Обновление документации

После добавления новых endpointов перегенерируйте документацию:

php artisan swagger:generate

6.4 Конфигурация Spectrum

// config/spectrum.php

return [
    'output' => 'public/docs',
    'title' => env('APP_NAME', 'Laravel API'),
    'description' => 'API Documentation',
    'version' => '1.0.0',
    'server_url' => env('APP_URL'),
    'servers' => [
        ['url' => env('APP_URL'), 'description' => 'Local server'],
    ],
    'security_schemes' => [
        'bearerAuth' => [
            'type' => 'http',
            'scheme' => 'bearer',
            'bearerFormat' => 'JWT',
        ],
    ],
    'default_security_scheme' => 'bearerAuth',
    'paths' => [
        'controllers' => ['App\\Http\\Controllers\\Api\\'],
    ],
];

7. Команды Artisan

7.1 Создание пользовательской команды

Структура директорий

Команды Artisan располагаются в директории app/Console/Commands/:

app/
├── Console/
│   └── Commands/
│       └── GenerateSwagger.php    # Наша команда

Создание команды

1. Создайте директорию Commands:

mkdir -p app/Console/Commands

2. Создайте файл команды:

# app/Console/Commands/GenerateSwagger.php

Пример кода команды

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use Symfony\Component\Yaml\Yaml;

class GenerateSwagger extends Command
{
    /**
     * Имя и сигнатура команды.
     * Используется для вызова: php artisan swagger:generate
     */
    protected $signature = 'swagger:generate';

    /**
     * Описание команды.
     * Показывается в списке команд: php artisan list
     */
    protected $description = 'Generate OpenAPI specification for the API';

    /**
     * Метод execute() - основная логика команды.
     * Вызывается при выполнении команды.
     */
    public function handle(): int
    {
        // Вывод информационного сообщения
        $this->info('Generating OpenAPI specification...');

        // Создание директории для документации
        $outputDir = public_path('docs');
        if (!File::exists($outputDir)) {
            File::makeDirectory($outputDir, 0755, true);
        }

        // Генерация спецификации
        $spec = $this->generateSpec();
        
        // Конвертация в YAML формат
        $yaml = Yaml::dump($spec, 4, 2);

        // Сохранение файла
        File::put($outputDir . '/openapi.yaml', $yaml);
        $this->info('OpenAPI specification saved to public/docs/openapi.yaml');

        // Возврат кода успеха
        return Command::SUCCESS;
    }

    /**
     * Метод для генерации структуры OpenAPI спецификации.
     * Можно создавать YAML или JSON вручную.
     */
    protected function generateSpec(): array
    {
        $appUrl = config('app.url', 'http://localhost');

        return [
            'openapi' => '3.0.0',
            'info' => [
                'title' => config('app.name', 'Laravel API'),
                'description' => 'API Documentation',
                'version' => '1.0.0',
            ],
            'servers' => [
                ['url' => $appUrl, 'description' => 'Local server'],
            ],
            'paths' => $this->generatePaths(),
            'components' => [
                'securitySchemes' => [
                    'bearerAuth' => [
                        'type' => 'http',
                        'scheme' => 'bearer',
                        'bearerFormat' => 'JWT',
                    ],
                ],
            ],
        ];
    }

    /**
     * Генерация секции paths для OpenAPI.
     */
    protected function generatePaths(): array
    {
        return [
            '/api/register' => [
                'post' => [
                    'tags' => ['Auth'],
                    'summary' => 'Register a new user',
                    // ... описание endpoint
                ],
            ],
            // ... другие endpoints
        ];
    }
}

Основные компоненты команды

Компонент Описание
$signature Имя команды (например, swagger:generate)
$description Описание для списка команд
handle() Основной метод выполнения команды
$this->info() Вывод информационного сообщения
$this->error() Вывод сообщения об ошибке
$this->warn() Вывод предупреждения
$this->question() Вывод вопроса
Command::SUCCESS Код успешного завершения
Command::FAILURE Код неудачного завершения

Методы для взаимодействия с пользователем

// Вывод текста
$this->info('Сообщение');        // Зеленый текст
$this->error('Ошибка');         // Красный текст
$this->warn('Внимание');        // Желтый текст
$this->line('Текст');           // Обычный текст

// Запрос подтверждения
if ($this->confirm('Продолжить?')) {
    // пользователь ответил "да"
}

// Выбор из списка
$choice = $this->choice('Выберите:', ['opt1', 'opt2', 'opt3'], 0);

// Ввод текста
$name = $this->ask('Введите имя:');
$password = $this->secret('Введите пароль:');  // Скрытый ввод

// Прогресс-бар
$bar = $this->output->createProgressBar(100);
$bar->start();
foreach ($items as $item) {
    // обработка
    $bar->advance();
}
$bar->finish();

7.2 Список команд

Команда Описание
php artisan swagger:generate Генерация OpenAPI спецификации
php artisan list Список всех команд
php artisan list api Список команд содержащих "api"

7.3 Пример использования

# Генерация документации
php artisan swagger:generate

# Очистка кэша конфигурации
php artisan config:clear

# Очистка кэша маршрутов
php artisan route:clear

# Просмотр списка команд
php artisan list

# Справка по команде
php artisan swagger:generate --help

7.4 Автоматическая регистрация

Laravel автоматически обнаруживает команды в директории app/Console/Commands. Для ручной регистрации добавьте в app/Console/Kernel.php:

// app/Console/Kernel.php

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        Commands\GenerateSwagger::class,
    ];

    /**
     * Define the application's command schedule.
     */
    protected function schedule(Schedule $schedule): void
    {
        // $schedule->command('swagger:generate')->daily();
    }

    /**
     * Register the commands for the application.
     */
    protected function commands(): void
    {
        $this->load(__DIR__.'/Commands');
    }
}

8. Политики доступа

8.1 PostPolicy

// app/Policies/PostPolicy.php

namespace App\Policies;

use App\Models\Post;
use App\Models\User;

class PostPolicy
{
    public function update(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }

    public function delete(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }
}

8.2 Регистрация политики

// app/Providers/AuthServiceProvider.php

namespace App\Providers;

use App\Models\Post;
use App\Models\User;
use App\Policies\PostPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    protected $policies = [
        Post::class => PostPolicy::class,
    ];

    public function boot(): void
    {
        $this->registerPolicies();
    }
}

9. Конфигурация .env

Убедитесь, что в файле .env указаны правильные настройки:

APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:...
APP_URL=http://la.test

DB_CONNECTION=sqlite

SESSION_DRIVER=database
SANCTUM_STATEFUL_DOMAINS=la.test,localhost,127.0.0.1

10. Тестирование API

10.1 Примеры запросов в Swagger UI

  1. Откройте http://la.test/api/docs
  2. Нажмите Authorize и введите ваш token
  3. Тестируйте endpoints прямо в браузере

10.2 Ручное тестирование

# Проверка списка маршрутов
php artisan route:list

# Запуск сервера
php artisan serve --host=la.test

# Запуск тестов
php artisan test

Troubleshooting

Проблема: 401 Unauthorized

Решение:

  1. Убедитесь, что передаёте правильный токен в заголовке Authorization
  2. Проверьте срок действия токена
  3. Убедитесь, что используете префикс Bearer

Проблема: CSRF token mismatch

Решение: Для SPA приложений добавьте домен в SANCTUM_STATEFUL_DOMAINS:

SANCTUM_STATEFUL_DOMAINS=la.test,localhost,127.0.0.1

Проблема: Таблица уже существует

Решение: Если миграция personal_access_tokens уже выполнена, пропустите её:

php artisan migrate --path=/database/migrations/2026_03_20_032532_create_personal_access_tokens_table.php --skip

Заключение

Теперь у вас есть полностью настроенный API с:

  • Laravel Sanctum для аутентификации
  • Token-based авторизация
  • API Resources для форматирования ответов
  • Form Requests для валидации
  • Swagger документация по адресу /api/docs
  • Политики доступа для постов

Для получения дополнительной информации обратитесь к официальной документации: