diff --git a/SANCTUM_SETUP_GUIDE.html b/SANCTUM_SETUP_GUIDE.html new file mode 100644 index 0000000..3400ff7 --- /dev/null +++ b/SANCTUM_SETUP_GUIDE.html @@ -0,0 +1,752 @@ +
+Данное руководство описывает процесс установки и настройки Laravel Sanctum для API аутентификации с использованием wadakatu/laravel-spectrum для автоматической генерации Swagger документации.
+composer require laravel/sanctum
+composer require wadakatu/laravel-spectrum
+# Публикация Sanctum
+php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
+
+# Публикация Spectrum (если требуется)
+php artisan vendor:publish --provider="Wadakatu\LaravelSpectrum\LaravelSpectrumServiceProvider"
+php artisan migrate
+++Примечание: Если таблица
+personal_access_tokensуже существует, пропустите этот шаг.
Добавьте трейт 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;
+
+ // ...
+}
+Обновите файл 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 {
+ //
+ })
+ ->withExceptions(function (Exceptions $exceptions): void {
+ //
+ })->create();
+++Важно: Не используйте
+statefulApi()для чистого token-based API, это вызовет ошибку 419 CSRF.
// 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,
+ ],
+];
+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
+Laravel Spectrum автоматически определяет структуру ответа из API Resources.
+// 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,
+ ];
+ }
+}
+// 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,
+ ];
+ }
+}
+Laravel Spectrum автоматически анализирует контроллеры для генерации документации.
+// app/Http/Controllers/Api/AuthController.php
+
+namespace App\Http\Controllers\Api;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\LoginRequest;
+use App\Http\Requests\RegisterRequest;
+use App\Http\Resources\UserResource;
+use App\Models\User;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Hash;
+
+class AuthController extends Controller
+{
+ /**
+ * Register a new user.
+ */
+ public function register(RegisterRequest $request): JsonResponse
+ {
+ $user = User::create([
+ 'name' => $request->name,
+ 'email' => $request->email,
+ 'password' => Hash::make($request->password),
+ ]);
+
+ $token = $user->createToken('auth-token')->plainTextToken;
+
+ return response()->json([
+ 'message' => 'User registered successfully',
+ 'user' => new UserResource($user),
+ 'token' => $token,
+ ], 201);
+ }
+
+ /**
+ * Login user.
+ */
+ public function login(LoginRequest $request): JsonResponse
+ {
+ $user = User::where('email', $request->email)->first();
+
+ if (!$user || !Hash::check($request->password, $user->password)) {
+ return response()->json([
+ 'message' => 'Invalid credentials',
+ ], 401);
+ }
+
+ $token = $user->createToken('auth-token')->plainTextToken;
+
+ return response()->json([
+ 'message' => 'Login successful',
+ 'user' => new UserResource($user),
+ 'token' => $token,
+ ]);
+ }
+
+ /**
+ * Logout user.
+ */
+ public function logout(Request $request): JsonResponse
+ {
+ $request->user()->currentAccessToken()->delete();
+
+ return response()->json([
+ 'message' => 'Logged out successfully',
+ ]);
+ }
+
+ /**
+ * Get authenticated user.
+ */
+ public function user(Request $request): JsonResponse
+ {
+ return response()->json([
+ 'user' => new UserResource($request->user()),
+ ]);
+ }
+}
+// app/Http/Controllers/Api/PostController.php
+
+namespace App\Http\Controllers\Api;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\StorePostRequest;
+use App\Http\Requests\UpdatePostRequest;
+use App\Http\Resources\PostCollection;
+use App\Http\Resources\PostResource;
+use App\Models\Post;
+use Illuminate\Http\JsonResponse;
+
+class PostController extends Controller
+{
+ /**
+ * Display a listing of posts.
+ */
+ public function index(): PostCollection
+ {
+ $posts = Post::with('user')->latest()->paginate(15);
+ return new PostCollection($posts);
+ }
+
+ /**
+ * Store a newly created post.
+ */
+ public function store(StorePostRequest $request): JsonResponse
+ {
+ $post = Post::create([
+ 'title' => $request->title,
+ 'content' => $request->content,
+ 'user_id' => $request->user()->id,
+ ]);
+
+ $post->load('user');
+
+ return response()->json([
+ 'message' => 'Post created successfully',
+ 'post' => new PostResource($post),
+ ], 201);
+ }
+
+ /**
+ * Display the specified post.
+ */
+ public function show(Post $post): JsonResponse
+ {
+ $post->load('user');
+ return response()->json([
+ 'post' => new PostResource($post),
+ ]);
+ }
+
+ /**
+ * Update the specified post.
+ */
+ public function update(UpdatePostRequest $request, Post $post): JsonResponse
+ {
+ $this->authorize('update', $post);
+
+ $post->update($request->validated());
+
+ return response()->json([
+ 'message' => 'Post updated successfully',
+ 'post' => new PostResource($post),
+ ]);
+ }
+
+ /**
+ * Remove the specified post.
+ */
+ public function destroy(Post $post): JsonResponse
+ {
+ $this->authorize('delete', $post);
+
+ $post->delete();
+
+ return response()->json([
+ 'message' => 'Post deleted successfully',
+ ]);
+ }
+}
+Laravel Spectrum автоматически определяет правила валидации из Form Requests.
+// 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'],
+ ];
+ }
+}
+// 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'],
+ ];
+ }
+}
+// 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'],
+ ];
+ }
+}
+// routes/api.php
+
+use App\Http\Controllers\Api\AuthController;
+use App\Http\Controllers\Api\PostController;
+use Illuminate\Support\Facades\Route;
+
+/*
+|--------------------------------------------------------------------------
+| API Routes
+|--------------------------------------------------------------------------
+*/
+
+// Swagger Documentation - редирект на Swagger UI
+Route::get('/docs', function () {
+ return redirect()->to('/spectrum/openapi.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');
+});
+| Метод | +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 |
+OpenAPI спецификация | +Нет | +
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"
+ }'
+curl -X POST http://la.test/api/login \
+ -H "Content-Type: application/json" \
+ -d '{
+ "email": "john@example.com",
+ "password": "password123"
+ }'
+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."
+ }'
+curl -X POST http://la.test/api/logout \
+ -H "Authorization: Bearer {token}"
+Документация Swagger UI доступна по адресу:
+{APP_URL}/api/docs
+Например: http://la.test/api/docs
Laravel Spectrum генерирует готовый HTML с Swagger UI:
+php artisan spectrum:generate --format=html
+Это создаёт файл public/spectrum/openapi.html.
При генерации с флагом --format=html symlink создаётся автоматически.
Если symlink отсутствует, создайте его вручную:
+ln -sf /home/user/www/lara/storage/app/spectrum /home/user/www/lara/public/spectrum
+public/
+└── spectrum/
+ ├── openapi.html # Swagger UI (генерируется с --format=html)
+ └── openapi.json # OpenAPI спецификация
+// routes/api.php
+
+// Swagger Documentation
+Route::get('/docs', function () {
+ return redirect()->to('/spectrum/openapi.html');
+});
+Laravel Spectrum поставляется с готовыми командами для работы с документацией.
+| Команда | +Описание | +
|---|---|
php artisan spectrum:generate |
+Генерация OpenAPI документации | +
php artisan spectrum:watch |
+Режим реального времени (port 8080) | +
php artisan spectrum:mock |
+Запуск mock сервера (port 8081) | +
php artisan spectrum:cache clear |
+Очистка кэша анализа | +
# Генерация документации в формате HTML
+php artisan spectrum:generate --format=html
+Вывод:
+🚀 Generating API documentation...
+🔍 Analyzing routes...
+Found 9 API routes
+📝 Generating OpenAPI specification...
+✅ Documentation generated: /home/user/www/lara/public/spectrum/openapi.html
+⏱️ Generation completed in 0.17 seconds
+💾 Cache: 5 files, 7.04 KB
+✅ Documentation generated successfully!
+# Запуск режима watch
+php artisan spectrum:watch
+http://localhost:8080# Запуск mock API сервера
+php artisan spectrum:mock
+http://localhost:8081После добавления новых endpointов:
+# Очистить кэш и перегенерировать
+php artisan spectrum:cache clear
+php artisan spectrum:generate
+// 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;
+ }
+}
+Политики автоматически обнаруживаются Laravel. Для ручной регистрации:
+// app/Providers/AuthServiceProvider.php
+
+namespace App\Providers;
+
+use App\Models\Post;
+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();
+ }
+}
+APP_NAME=Laravel
+APP_ENV=local
+APP_KEY=base64:...
+APP_URL=http://la.test
+
+DB_CONNECTION=sqlite
+
+SESSION_DRIVER=database
+# Проверка списка маршрутов
+php artisan route:list --path=api
+
+# Генерация документации
+php artisan spectrum:generate
+
+# Запуск сервера
+php artisan serve
+Решение:
+Authorization: Bearer {token}BearerРешение:
+Уберите statefulApi() из middleware в bootstrap/app.php:
->withMiddleware(function (Middleware $middleware): void {
+ // Не используйте statefulApi() для token-based API
+})
+Решение:
+Не используйте throttleApi():
// Неправильно
+$middleware->throttleApi();
+
+// Правильно (оставить пустым или удалить)
+$middleware->api();
+Теперь у вас есть полностью настроенный API с:
+/api/docsКоманды для работы:
+php artisan spectrum:generate # Генерация документации
+php artisan spectrum:watch # Режим разработки
+php artisan spectrum:mock # Mock сервер
+Документация: http://la.test/api/docs