Если вы когда-нибудь задумывались, как устроены современные веб-сервисы, которые отдают данные мобильным приложениям или фронтенду, то ответ прост — через API. А если точнее, через RESTful API. Сегодня я расскажу, как создать такое API на Yii2, и главное — как защитить его с помощью авторизации по токенам. Это не rocket science, но есть свои подводные камни, которые мы обойдём вместе.

Digital-студия WNDER

Что такое RESTful API и зачем он нужен

REST — это архитектурный стиль, где всё крутится вокруг ресурсов. Ресурс — это, например, пользователь, заказ или товар. Каждый ресурс имеет URL, а действия над ним выполняются через HTTP-методы: GET (получить), POST (создать), PUT (обновить), DELETE (удалить). Всё просто и логично.

Yii2 — это мощный PHP-фреймворк, который из коробки поддерживает RESTful API. Вам не нужно писать кучу кода — достаточно настроить контроллеры и правила маршрутизации. Но самое интересное — авторизация. Токен — это как ключ от квартиры. Выдал ключ — и человек может заходить. Отобрал — и доступ закрыт.

В нашем примере мы создадим API для управления списком задач (TODO). Без авторизации любой сможет читать, менять, удалять задачи. А с токенами — только те, у кого есть ключ.

Настройка окружения и базовая структура

Предположим, у вас уже есть установленный Yii2 (advanced или basic — не важно, главное, чтобы был компонент REST). Если нет — установите через Composer:

composer create-project --prefer-dist yiisoft/yii2-app-advanced api-demo

После установки перейдите в папку проекта и выполните миграции для создания таблиц пользователей. В advanced-шаблоне уже есть таблица user, но мы добавим поле для токена. Создадим миграцию:

php yii migrate/create add_token_to_user_table

В сгенерированном файле миграции добавьте поле:

public function safeUp()
{
    $this->addColumn('user', 'access_token', $this->string(255)->notNull()->defaultValue(''));
}

public function safeDown()
{
    $this->dropColumn('user', 'access_token');
}

Затем выполните миграцию:

php yii migrate

Теперь у нас есть поле для токена. Двигаемся дальше.

Создаём модель пользователя и проверку токена

В Yii2 авторизация по токенам реализуется через интерфейс yii\web\IdentityInterface. Наша модель User должна реализовать метод findIdentityByAccessToken(). Откройте файл common/models/User.php и добавьте следующий код:

use yii\web\IdentityInterface;

class User extends ActiveRecord implements IdentityInterface
{
    // ... другие методы ...

    public static function findIdentityByAccessToken($token, $type = null)
    {
        return static::findOne(['access_token' => $token]);
    }

    // Остальные методы интерфейса можно оставить как есть
}

Теперь при запросе с токеном Yii2 найдёт пользователя. Но этого мало — нужно настроить само приложение, чтобы оно принимало токен из запроса.

Конфигурируем REST API и авторизацию

В Yii2 REST API настраивается в конфигурационном файле приложения (например, api/config/main.php для advanced-шаблона). Пропишем компонент user и правила маршрутизации:

'components' => [
    'user' => [
        'identityClass' => 'common\models\User',
        'enableAutoLogin' => false,
        'enableSession' => false, // REST API не использует сессии
        'loginUrl' => null, // не перенаправлять на страницу входа
    ],
    'urlManager' => [
        'enablePrettyUrl' => true,
        'showScriptName' => false,
        'rules' => [
            [
                'class' => 'yii\rest\UrlRule',
                'controller' => 'task',
                'extraPatterns' => [
                    'POST login' => 'login', // эндпоинт для получения токена
                ],
            ],
        ],
    ],
],

Обратите внимание на enableSession => false — это важно, потому что REST API не должен хранить состояние между запросами. Каждый запрос — как новый гость в ресторане: пришёл, поел, ушёл. Никакой памяти.

Теперь создадим контроллер для задач. В Yii2 есть готовый базовый класс yii\rest\ActiveController, который уже поддерживает все CRUD-операции. Нам нужно только переопределить поведение для авторизации:

namespace api\controllers;

use yii\rest\ActiveController;
use yii\filters\auth\HttpBearerAuth;

class TaskController extends ActiveController
{
    public $modelClass = 'common\models\Task';

    public function behaviors()
    {
        $behaviors = parent::behaviors();
        $behaviors['authenticator'] = [
            'class' => HttpBearerAuth::className(),
        ];
        return $behaviors;
    }
}

Здесь мы подключили аутентификацию через Bearer-токен. Это значит, что клиент должен передавать заголовок Authorization: Bearer <токен>. Если токен неверный или его нет — Yii2 вернёт ошибку 401.

Создаём модель Task и эндпоинт для логина

Нам нужна модель Task. Создайте её в common/models/Task.php:

namespace common\models;

use yii\db\ActiveRecord;

class Task extends ActiveRecord
{
    public static function tableName()
    {
        return 'task';
    }

    public function rules()
    {
        return [
            [['title', 'description'], 'required'],
            [['title'], 'string', 'max' => 255],
            [['description'], 'string'],
            [['status'], 'boolean'],
        ];
    }
}

Не забудьте создать таблицу task через миграцию:

php yii migrate/create create_task_table

Теперь добавим эндпоинт для логина. Это будет отдельный контроллер, который выдаёт токен по логину и паролю. Создадим api/controllers/AuthController.php:

namespace api\controllers;

use Yii;
use yii\rest\Controller;
use common\models\User;
use yii\web\UnauthorizedHttpException;

class AuthController extends Controller
{
    public function actionLogin()
    {
        $request = Yii::$app->request;
        $username = $request->post('username');
        $password = $request->post('password');

        $user = User::findByUsername($username);
        if (!$user || !$user->validatePassword($password)) {
            throw new UnauthorizedHttpException('Неверный логин или пароль');
        }

        // Генерируем токен (например, через random_bytes)
        $user->access_token = bin2hex(random_bytes(32));
        $user->save();

        return ['access_token' => $user->access_token];
    }
}

Теперь у нас есть всё для работы. Давайте протестируем.

Практический пример: как всё это работает

Представьте, что вы — мобильное приложение. Чтобы получить список задач, вы отправляете GET-запрос на /task с заголовком Authorization: Bearer <токен>. Если токен верный — сервер вернёт JSON со списком задач. Если нет — ошибку 401.

Чтобы получить токен, отправляете POST на /auth/login с логином и паролем. В ответ получите токен. Вот пример запроса через curl:

# Получаем токен
curl -X POST http://api.example.com/auth/login \
  -d 'username=admin&password=123456'

# Ответ: {"access_token":"abc123..."}

# Запрос задач с токеном
curl http://api.example.com/task \
  -H "Authorization: Bearer abc123..."

Всё работает. Но есть нюанс: токен хранится в базе как есть. Если базу украдут — злоумышленник получит доступ к аккаунтам. Поэтому лучше хранить хэш токена. Но это уже тема для отдельной статьи.

Правило: Никогда не храните токены в открытом виде. Храните их хэш (например, через hash('sha256', $token)). Это защитит пользователей даже в случае утечки базы данных.

Безопасность и дополнительные советы

Авторизация по токенам — это хорошо, но есть ещё несколько моментов, о которых стоит помнить:

  • Используйте HTTPS. Токен передаётся в заголовке, и если соединение не защищено, его можно перехватить.
  • Устанавливайте срок действия токена. Например, через 30 дней токен становится недействительным, и пользователь должен заново пройти аутентификацию.
  • Ограничьте количество попыток входа (rate limiting). Yii2 имеет встроенный фильтр yii\filters\RateLimiter — обязательно используйте.
  • Для критических операций (удаление, изменение) проверяйте права доступа (RBAC).

Вот как добавить rate limiter в контроллер:

use yii\filters\RateLimiter;

public function behaviors()
{
    $behaviors = parent::behaviors();
    $behaviors['rateLimiter'] = [
        'class' => RateLimiter::className(),
        'enableRateLimitHeaders' => true,
    ];
    return $behaviors;
}

Что в итоге

Мы создали RESTful API на Yii2 с авторизацией по токенам. Всё оказалось не так страшно, как казалось. Главные шаги:

  • Добавили поле access_token в таблицу user.
  • Реализовали findIdentityByAccessToken() в модели User.
  • Настроили компонент user и urlManager для REST.
  • Создали контроллер Task с аутентификацией через Bearer-токен.
  • Добавили эндпоинт для логина.

Теперь вы можете расширять это API, добавлять новые ресурсы и методы. Главное — не забывайте про безопасность и тестирование. Удачи в кодинге!

Студия WNDER