laravel-12/SANCTUM_SETUP_GUIDE.md

939 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Руководство по установке и настройке Laravel Sanctum с API авторизацией
## Описание
Данное руководство описывает процесс установки и настройки **Laravel Sanctum** для API аутентификации с использованием **wadakatu/laravel-spectrum** для генерации Swagger документации.
---
## Содержание
1. [Установка пакетов](#1-установка-пакетов)
2. [Настройка Sanctum](#2-настройка-sanctum)
3. [Структура API](#3-структура-api)
4. [API Endpoints](#4-api-endpoints)
5. [Использование API](#5-использование-api)
6. [Swagger документация](#6-swagger-документация)
7. [Команды Artisan](#7-команды-artisan)
---
## 1. Установка пакетов
### 1.1 Установка Laravel Sanctum
```bash
composer require laravel/sanctum
```
### 1.2 Установка wadakatu/laravel-spectrum
```bash
composer require wadakatu/laravel-spectrum
```
### 1.3 Публикация конфигураций
```bash
# Публикация Sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
# Публикация Spectrum
php artisan vendor:publish --provider="Wadakatu\LaravelSpectrum\LaravelSpectrumServiceProvider"
```
### 1.4 Запуск миграций
```bash
php artisan migrate
```
> **Примечание:** Если таблица `personal_access_tokens` уже существует, пропустите этот шаг.
---
## 2. Настройка Sanctum
### 2.1 Обновление модели User
Добавьте трейт `HasApiTokens` в модель `User`:
```php
// 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`:
```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`. Основные настройки:
```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** - форматирование данных пользователя:
```php
// 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** - форматирование данных поста:
```php
// 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** - валидация регистрации:
```php
// 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** - валидация входа:
```php
// 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** - валидация создания поста:
```php
// 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** - валидация обновления поста:
```php
// 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 Маршруты
```php
// 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 Регистрация
**Запрос:**
```bash
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"
}'
```
**Ответ:**
```json
{
"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 Вход
**Запрос:**
```bash
curl -X POST http://la.test/api/login \
-H "Content-Type: application/json" \
-d '{
"email": "john@example.com",
"password": "password123"
}'
```
**Ответ:**
```json
{
"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 Создание поста
**Запрос:**
```bash
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."
}'
```
**Ответ:**
```json
{
"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 Обновление поста
**Запрос:**
```bash
curl -X PUT http://la.test/api/posts/1 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {token}" \
-d '{
"title": "Updated Title"
}'
```
### 5.5 Удаление поста
**Запрос:**
```bash
curl -X DELETE http://la.test/api/posts/1 \
-H "Authorization: Bearer {token}"
```
### 5.6 Выход
**Запрос:**
```bash
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ов перегенерируйте документацию:
```bash
php artisan swagger:generate
```
### 6.4 Конфигурация Spectrum
```php
// 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:**
```bash
mkdir -p app/Console/Commands
```
**2. Создайте файл команды:**
```bash
# app/Console/Commands/GenerateSwagger.php
```
#### Пример кода команды
```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` | Код неудачного завершения |
#### Методы для взаимодействия с пользователем
```php
// Вывод текста
$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 Пример использования
```bash
# Генерация документации
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`:
```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
```php
// 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 Регистрация политики
```php
// 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` указаны правильные настройки:
```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 Ручное тестирование
```bash
# Проверка списка маршрутов
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`:
```env
SANCTUM_STATEFUL_DOMAINS=la.test,localhost,127.0.0.1
```
### Проблема: Таблица уже существует
**Решение:**
Если миграция `personal_access_tokens` уже выполнена, пропустите её:
```bash
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`
- ✅ Политики доступа для постов
Для получения дополнительной информации обратитесь к официальной документации:
- [Laravel Sanctum](https://laravel.com/docs/sanctum)
- [wadakatu/laravel-spectrum](https://github.com/wadakatu/laravel-spectrum)