Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Токены API и ограничение запросов

Мобильное приложение или чужой сервис не могут войти через форму и хранить сессию. Они получают токен — длинную случайную строку — и присылают его в каждом запросе. В Laravel токены выдаёт пакет Sanctum.

Установка

php artisan install:api

Команда ставит Sanctum, создаёт таблицу токенов и файл routes/api.php. В модель пользователя добавьте трейт:

use Laravel\Sanctum\HasApiTokens;
 
class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
}

Выдача токена

// routes/api.php
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
 
Route::post('/login', function (Request $request) {
    $data = $request->validate([
        'email' => 'required|email',
        'password' => 'required',
        'device' => 'required',
    ]);
 
    $user = User::where('email', $data['email'])->first();
 
    if (!$user || !Hash::check($data['password'], $user->password)) {
        throw ValidationException::withMessages([
            'email' => 'Неверная почта или пароль.',
        ]);
    }
 
    return ['token' => $user->createToken($data['device'])->plainTextToken];
});

Защищённые маршруты

Route::middleware('auth:sanctum')->group(function () {
    Route::get('/user', fn (Request $request) => $request->user());
    Route::apiResource('posts', PostController::class);
});

Клиент присылает токен в заголовке:

curl https://example.com/api/user -H "Authorization: Bearer 1|Xk3f9aB..." -H "Accept: application/json"

Без токена или с неверным токеном ответ — код 401.

Права токена

Токену можно разрешить только часть действий:

$token = $user->createToken('reports', ['posts:read'])->plainTextToken;
public function store(Request $request)
{
    if (!$request->user()->tokenCan('posts:write')) {
        abort(403);
    }
 
    // …
}

Отзыв

$request->user()->currentAccessToken()->delete();   // выход на этом устройстве
 
$request->user()->tokens()->delete();               // выход везде

Ограничение частоты запросов

Чтобы один клиент не перегрузил сервер, число запросов ограничивают посредником throttle:

Route::middleware(['auth:sanctum', 'throttle:60,1'])->group(function () {
    // не больше 60 запросов в минуту
});

При превышении ответ — код 429 с заголовком Retry-After.

Свои правила

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
 
public function boot(): void
{
    RateLimiter::for('uploads', function (Request $request) {
        return Limit::perHour(20)->by($request->user()?->id ?: $request->ip());
    });
}
Route::post('/files', [FileController::class, 'store'])->middleware('throttle:uploads');

Метод by() задаёт, по чему считать: по пользователю или по адресу.

Вход — тоже под ограничение

Route::post('/login', [AuthController::class, 'login'])->middleware('throttle:5,1');

Пять попыток в минуту делают подбор пароля бессмысленным.

Частая ошибка

Значение plainTextToken доступно только в момент создания: в базе хранится хеш, и показать токен повторно нельзя. Отдайте его клиенту сразу. И токен — такой же секрет, как пароль: не записывайте его в журнал, не передавайте в адресе запроса и задайте срок жизни настройкой expiration в config/sanctum.php.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.