23 KiB
Руководство по установке и настройке Laravel Sanctum с API авторизацией
Описание
Данное руководство описывает процесс установки и настройки Laravel Sanctum для API аутентификации с использованием wadakatu/laravel-spectrum для генерации Swagger документации.
Содержание
- Установка пакетов
- Настройка Sanctum
- Структура API
- API Endpoints
- Использование API
- Swagger документация
- Команды 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
- Откройте
http://la.test/api/docs - Нажмите Authorize и введите ваш token
- Тестируйте endpoints прямо в браузере
10.2 Ручное тестирование
# Проверка списка маршрутов
php artisan route:list
# Запуск сервера
php artisan serve --host=la.test
# Запуск тестов
php artisan test
Troubleshooting
Проблема: 401 Unauthorized
Решение:
- Убедитесь, что передаёте правильный токен в заголовке
Authorization - Проверьте срок действия токена
- Убедитесь, что используете префикс
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 - ✅ Политики доступа для постов
Для получения дополнительной информации обратитесь к официальной документации: