В современном мире веб-разработки API — это основа любого мобильного приложения или SPA. А безопасность — головная боль каждого разработчика. JWT (JSON Web Token) стал стандартом де-факто для аутентификации в API. В этой статье я расскажу, как быстро и без боли настроить API на Yii2 с JWT-аутентификацией.
Что такое JWT и зачем он нужен?
JWT — это JSON Web Token. Звучит страшно, но на деле это просто строка, которая содержит зашифрованную информацию о пользователе. Представьте, что вы даёте другу пропуск в клуб. В пропуске написано, кто вы и до какого времени действует пропуск. JWT работает так же: сервер выдаёт токен, клиент его хранит и при каждом запросе показывает. Сервер проверяет подпись и пускает или не пускает.
Главное преимущество JWT — он не требует хранения сессий на сервере. Всё, что нужно, уже внутри токена. Это удобно для масштабирования и микросервисов.
Установка и настройка Yii2
Предположим, у вас уже есть проект на Yii2. Если нет — создайте его через Composer:
composer create-project --prefer-dist yiisoft/yii2-app-basic api-jwt
cd api-jwt
Теперь установим библиотеку для работы с JWT. Есть несколько вариантов, но я рекомендую firebase/php-jwt — она простая и надёжная.
composer require firebase/php-jwt
Теперь настроим приложение для работы в режиме API. В файле config/web.php добавьте компонент request с парсером JSON:
'request' => [
'parsers' => [
'application/json' => 'yii\web\JsonParser',
],
],
И включите URL-менеджер для красивого роутинга:
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
'POST auth/login' => 'auth/login',
'POST auth/signup' => 'auth/signup',
'GET user/profile' => 'user/profile',
],
],
Создаём модель пользователя и контроллер аутентификации
Для начала создадим модель User. Если вы используете стандартную таблицу user из basic-шаблона, она нам подойдёт. Но убедитесь, что в ней есть поля username, password_hash и auth_key (последний не обязателен, но пригодится).
Теперь создадим контроллер AuthController с действиями login и signup.
<?php
namespace app\controllers;
use Yii;
use yii\rest\Controller;
use app\models\User;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use yii\web\Response;
class AuthController extends Controller
{
public $enableCsrfValidation = false;
public function actionLogin()
{
$request = Yii::$app->request;
$username = $request->post('username');
$password = $request->post('password');
$user = User::findByUsername($username);
if (!$user || !$user->validatePassword($password)) {
Yii::$app->response->statusCode = 401;
return ['error' => 'Неверный логин или пароль'];
}
$token = $this->generateJwt($user);
return ['token' => $token];
}
public function actionSignup()
{
$request = Yii::$app->request;
$user = new User();
$user->username = $request->post('username');
$user->setPassword($request->post('password'));
$user->generateAuthKey();
if ($user->save()) {
$token = $this->generateJwt($user);
return ['token' => $token];
}
Yii::$app->response->statusCode = 400;
return ['errors' => $user->getErrors()];
}
private function generateJwt($user)
{
$payload = [
'iss' => 'your-domain.com',
'sub' => $user->id,
'iat' => time(),
'exp' => time() + 3600, // токен живёт 1 час
];
return JWT::encode($payload, Yii::$app->params['jwtSecretKey'], 'HS256');
}
}
Обратите внимание: в config/params.php нужно добавить секретный ключ:
'jwtSecretKey' => 'your-very-secret-key-change-me',
Защита API с помощью фильтра JWT
Теперь самое интересное: как защитить другие эндпоинты? В Yii2 для этого есть фильтры. Создадим фильтр JwtAuth.
<?php
namespace app\filters;
use Yii;
use yii\base\ActionFilter;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use app\models\User;
class JwtAuth extends ActionFilter
{
public function beforeAction($action)
{
$authHeader = Yii::$app->request->headers->get('Authorization');
if (!$authHeader) {
Yii::$app->response->statusCode = 401;
Yii::$app->response->data = ['error' => 'Требуется токен'];
return false;
}
$token = str_replace('Bearer ', '', $authHeader);
try {
$decoded = JWT::decode($token, new Key(Yii::$app->params['jwtSecretKey'], 'HS256'));
$user = User::findIdentity($decoded->sub);
if (!$user) {
throw new \Exception('Пользователь не найден');
}
Yii::$app->user->setIdentity($user);
} catch (\Exception $e) {
Yii::$app->response->statusCode = 401;
Yii::$app->response->data = ['error' => 'Неверный токен: ' . $e->getMessage()];
return false;
}
return parent::beforeAction($action);
}
}
Теперь применим этот фильтр в контроллере, который должен быть защищён. Например, UserController:
class UserController extends Controller
{
public $enableCsrfValidation = false;
public function behaviors()
{
return [
'jwtAuth' => [
'class' => \app\filters\JwtAuth::class,
],
];
}
public function actionProfile()
{
return Yii::$app->user->identity;
}
}
Теперь при запросе к /user/profile без токена вы получите 401, а с токеном — данные пользователя.
Практические советы и лайфхаки
- Время жизни токена. Не делайте его слишком большим. Час — оптимально. Для долгоживущих сессий используйте refresh-токены.
- Хранение токена на клиенте. Лучше всего в httpOnly cookie, чтобы избежать XSS. Но если у вас SPA, можно использовать localStorage, но тогда защититесь от CSRF.
- Обработка ошибок. Всегда возвращайте понятные сообщения об ошибках в JSON. Клиент должен знать, что пошло не так.
- Тестирование. Используйте Postman или Insomnia для проверки эндпоинтов. Не забудьте про автоматические тесты.
Вот таблица с основными параметрами JWT, которые вы можете использовать в payload:
| Параметр | Описание | Обязательный |
|---|---|---|
| iss | Издатель токена (например, ваш домен) | Нет |
| sub | Идентификатор пользователя | Да |
| iat | Время выдачи токена (Unix timestamp) | Нет |
| exp | Время истечения токена (Unix timestamp) | Да |
| nbf | Токен действителен не раньше этого времени | Нет |
Ещё один лайфхак: если вы используете HTTPS (а вы должны!), то токен передаётся в заголовке Authorization: Bearer <token>. Это стандарт, и его поддерживают все клиенты.
Заключение
Мы разобрали, как создать API на Yii2 с JWT-аутентификацией. Это не так сложно, как кажется. Главное — понять принцип работы токенов и правильно настроить фильтры. Теперь вы можете защитить свои эндпоинты и спать спокойно.
Не забывайте обновлять токены, использовать HTTPS и не хранить секреты в открытом виде. Удачи в разработке!


