Аутентификация, авторизация и разграничение прав в REST API на Yii2

13 июня, 2015
Метки: ,

С основами создания REST API мы разобрались в прошлой статье. В этой статье поговорим о способах авторизации, аутентификации и разграничении прав в REST-приложениях на Yii2.
Для начала разберемся с терминами:
Аутентификация — проверка подлинности источника запроса. Т.е способ убедиться, что запрос поступил именно от данного пользователя. Сюда относятся проверка пары логин/пароль.
Идентификация — поиск пользователя по уникальному ключу-идентификатору.
Авторизация — наделение пользователя правами на выполнение определенных действий.
Идеология REST API основана на том, что сервис не хранит промежуточных данных, сессии и тому подобных промежуточных данных о клиенте. Идентификатор пользователя необходимо передавать при каждом запросе, требующим авторизации.

Настраиваем аутентификацию для REST API

Во-первых, т.к нам не нужно хранить данные о пользователе между запросами, необходимо отключить enableSession в настройках компонента user

 'user' => [
            'identityClass' => 'app\models\User',
            'enableSession' => false
        ],

Далее нужно настроить способ аутентификации (подробнее о них поговорим ниже), через поведения контроллера.

use yii\rest\Controller;

class RestController extends Controller {

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

В примере выше видно, что для аутентификатора можно использовать параметры only и except, как для любого другого ActionFilter, для того, чтобы задать необходимость аутентификации только для определенных action или исключить аутентификацию для некоторых action.
Теперь остается только проверить, что клиент отправляет правильные заголовки иначе будет получать UnauthorizedHttpException (код 401)

header('Authorization: Bearer FFFF70it7tzNsHddEiq0BZ0i-OU8S3xV');

Три способа аутентификации в Yii2

Yii2 реализовано 3 класса для аутентификации пользователя:

  • HttpBasicAuth — реализация базовой Http аутентификации. Пара логин пароль передаются в виде строки login:password закодированной с помощью функции base64
    Authorization: Basic YWRtaW46c2VjcmV0
    

    Так как это не метод шифрования, а способ кодирования, предназначенный для обеспечения надежности передачи данных по сети, использовать данный вид аутентификации стоит только при безопасном зашифрованном (https) соединении.
    В Yii2 необходимо настроить переменную auth, прописав в нее метод, который по паре логина и пароля будет возвращать экземпляр класса User.

            $behaviors = parent::behaviors();
            $behaviors['authenticator']['class'] = HttpBasicAuth::className();
            $behaviors['authenticator']['auth'] = function ($username, $password) {
              return \app\models\User::findOne([
                  'username' => $username,
                  'password' => $password,
              ]);
          };
    

    Иначе поле login будет восприниматься как токен и пользователь будет искаться так:

     $class = $this->identityClass;
     $identity = $class::findIdentityByAccessToken($token, $type);
    

    Параметр password в данном случае вообще игнорируется.

  • HttpBearerAuth — реализация аутентификации по токену (HTTP Bearer token). Для авторизации с помощью токена нужно передать его в http заголовке
    header('Authorization: Bearer FFFF70it7tzNsHddEiq0BZ0i-OU8S3xV');
    

    Токен — это более безопасный способ авторизации, т.к вы можете задать время жизни такому токену, генерировать его и шифровать надежными криптографическими методами, а так же генерировать уникальный токен при каждом обращении. Правила валидации и проверки токена нужно реализовать в методе findIdentityByAccessToken класса User.

  • QueryParamAuth — пожалуй самый простой из имеющихся способов авторизации. Работает по тому-же принципу, что и HttpBearerAuth, т.е. идентифицирует пользователя по токену, только в данном случае токен передается в строке запроса, как GET параметр. По умолчанию параметр называется «access-token», но это значение можно изменить
    //  site?access-token=FFFF70it7tzNsHddEiq0BZ0i-OU8S3xV 
    $behaviors = parent::behaviors();
    $behaviors['authenticator']['class'] = QueryParamAuth::className();
    $behaviors['authenticator']['tokenParam'] = 'hash';
    //  site?hash=FFFF70it7tzNsHddEiq0BZ0i-OU8S3xV 
    
  • CompositeAuth — это класс, который сам не реализует ни один из методов аутентификации, но позволяет одновременно использовать несколько типов из описанных выше.
    $behaviors = parent::behaviors();
    $behaviors['authenticator']['class'] = CompositeAuth::className();
    $behaviors['authenticator']['authMethods'] = [
                      \yii\filters\auth\HttpBasicAuth::className(),
                      [
                          'class' => \yii\filters\auth\QueryParamAuth::className(),
                          'tokenParam' => 'token'
                      ]
                     ];
    

    В данном случае Yii попытается найти и залогинить пользователя поочередно по каждому из методов.

  • Если авторизация прошла успешно, то ID авторизованного юзера доступно через Yii::$app->user->getId();

Разграничение прав в REST API на Yii2

Разграничение прав доступа для REST API ничем не отличается от разграничений прав в обычном приложении. Так как я уже подробно описывала RBAC в Yii2, повторяться не буду.

$behaviors[ 'access'] = [
            'class' => AccessControl::className(),
            'rules' => [
                [
                    'allow' => true,
                    'roles' => ['@'],
                ],
            ],
        ];

Если остались вопросы — задавайте их в комментариях, а я постараюсь на них ответить.


Метки: ,

Оставить комментарий

21 комментарий »

    Дмитрий

    А как проверять право на действие не с помощью фильтров AccessControl, а с помощью проверки конкретного действия? По типу if(Yii::$app->user->can(‘deleteUser’))?

      Developer

      Вы сами ответили на свой вопрос if(Yii::$app->user->can(«правило_или_разрешение»)). Более того, фильтр AccessControl вызывает в том числе метод matchRole, который использует все тот-же $user->can($role).
      Повторюсь, разграничение прав доступа для REST API ничем не отличается от разграничений для обычных приложений. Отличается способ, по которому восстанавливается текущий юзер, тк нет сессии и кук. А все остальное точно так-же как и в обычных приложениях.


    я

    какой смысл излагать документацию?

      Developer

      Ммм, какой вообще смысл обсуждать что-либо в блогах и на форумах. Есть же документация 🙂
      Я пытаюсь рассказать по-проще чем в документации. И надеюсь это кому-нибудь пригодится. Важность документации при этом ничуть не занижая. Но в Yii пока лучшая документация — это исходники.


    dev

    Для проверки через user->can в преднаписанных экшенах получается нужно их переопределять и самому проверять правила?
    if(Yii::$app->user->can(«правило_или_разрешение»)){
    parent::index();
    … };


    dev

    Сам ответил.. Нужно переопределить checkAccess..
    public function checkAccess($action, $model = null, $params = [])
    {
    if($action == ‘index’)
    {
    if(!Yii::$app->user->can(‘Rule’))
    {
    throw new \yii\web\HttpException(403);
    }

    }


    }


    Роман

    Здравствуйте, к сожалению статья не очень понятная, в отличии от предыдущей. Как мне проверять логин и пароль (клиент отправляет в JSON формате) в случае если такой есть отдавать ему его токен?

      Developer

      В статье речь о HTTP-авторизации, т.е об авторизации через HTTP заголовки. Если клиент отправляет логин/пароль в json формате в теле запроса (что совсем не секьюрно), то вам нужен простой action, который будет получать данные, проверять логин/пароль как в стандартом actionLogin, но не логинеть юзера, а возвращать токен, по которому потом можно будет идентифицировать юзера. Дальше клиент будет передавать уже этот токен.
      То, что данные в формате Json для Yii вообще не страшно. Пропишите в конфиге компонента request:

      //в конфиге
      'parsers' => [
                      'application/json' => 'yii\web\JsonParser',
                  ],
      //получение значений:
       $params = Yii::$app->request->getBodyParams();
      

      И yii сам поймет как распарсить данные в зависимости от сontentType.


    Александр

    А у меня такой момент рест сервис возвращается по ссылка с виндовой авторизацией в всплывающем окне как мне тут авторизоваться ?

      Developer

      «Виндовая» авторизация это Basic авторизация, см HttpBasicAuth. Отправлять base64 (логин:пароль) в http заголовках.


    Сергей

    Мне нужно авторизоваться через логи и пароль а потом по токену работать с апи как быть?

      Developer

      Yii позволяет подключить сразу несколько способов авторизации (см CompositeAuth). Затем в зависимости от полученных заголовков управление получит тот или иной класс. К примеру первый запрос с basic авторизацией (логин и пароль, зашифрованные в base64) в ответ API вернет токен. Дальше все запросы с Bearer заголовком с данным токеном.


    Юрий

    Есть ли какой-то встроенный механизм для получения токена или его нужно самому писать? если есть, то как найти url? где задается время жизни токена?

      Developer

      Все на усмотрение разработчика. И это правильно: разного уровня проекты нуждаются в разном уровне защиты. Как правило токены генерируются случайным образом. В некоторых системах токены обновляются при каждом запросе, а в некоторых вообще не устаревают. Где-то токен обновится так, что юзер даже этого не заметит, а где-то ему придется снова вводить логин/пароль чтобы получить свежий токен. Все зависит от приложения и его потребностей 🙂


    Alexey

    Автору респект за

     $behaviors = parent::behaviors();
      $behaviors['authenticator']['class'] = HttpBasicAuth::className();
      $behaviors['authenticator']['auth'] = function ($username, $password) {
        return \app\models\User::findOne([
            'username' => $username,
            'password' => $password,
        ]);
    };
    

    но тут многие поломают голову, т к пароль не хранится в открытом виде, нужно заменить на:

    $u = \app\models\User::findOne([
    				        'username' => $username,
    				    ]);
    					return (Password::validate($password, $u->password_hash)) ? $u : null;
    

    Александр

    HttpBasicAuth означает что юзеру будет каждый раз(или только первый?) вываливаться типовое окно авторизации юзер-пароль как при использовании .htpassw или как там его в апаче?..

      Developer

      Нет, HttpBasicAuth это способ программно передать юзер-пароль на страницу с использование basic авторизации. Если вы открываете эту страницу в браузере — то вводите логин-пароль, если программно стучитесь (API) на эту странцу, то логин-пароль нужно передать в заголовках иначе доступ будет запрещен.


    Greva

    Можна ли как то прикрутить Yii::t()

      Developer

      При REST запросе, важно не столько сообщение, сколько сам HTTP код ответа. Если ну очень хочется переопределить сообщение, то нужно:
      1) отнаследоваться от класса аутентификации (к примеру HttpBearerAuth) и добавить в него метод

       public function handleFailure($response)
          {
              throw new UnauthorizedHttpException(Yii::t('app', 'Your request was made with invalid credentials.'));
          }
      

      т.е тем самым перекрыть этот метод у родителя класса HttpBearerAuth (родитель: yii\filters\auth\AuthMethod)
      2) в контроллере прописать использование своего класса вместо оригинального HttpBearerAuth

      А вообще по хорошему, локализация сообщения должна быть на стороне клиента. Rest вернул 401 заголовок — клиент понял что это означает «не авторизован» и вывел пользователю сообщение на том языка, на каком пользователю нужно.


    Константин

    Здравствуйте, что-то прочтя документацию и ваш гайд, я все равно не понял где прописывается header. Можете подробнее описать этот момент?

      Developer

      Эта статья о том, как ограничить доступ к страницам или API по паролю. Для этого не нужно прописывать header-ы. Yii проверит есть ли нужные заголовки в запросе и проверит пароли, прежде чем предоставит доступ к вашему API или странице.
      Если вы спрашиваете о том, как в ответе Yii передать заголовки авторазиции (например если ваш API вызывает сторонний сервер и ему нужно убедиться что ответ пришел именно от вас), то примерно так:

       Yii::$app->response->headers->set('Authorization', ['Bearer FFFF70it7tzNsHddEiq0BZ0i-OU8S3xV'])
      

Оставить комментарий:









Копирование материалов разрешено при наличии активной ссылки на источник