С основами создания REST API мы разобрались в прошлой статье. В этой статье поговорим о способах авторизации, аутентификации и разграничении прав в REST-приложениях на Yii2.
Для начала разберемся с терминами:
Аутентификация — проверка подлинности источника запроса. Т.е способ убедиться, что запрос поступил именно от данного пользователя. Сюда относятся проверка пары логин/пароль.
Идентификация — поиск пользователя по уникальному ключу-идентификатору.
Авторизация — наделение пользователя правами на выполнение определенных действий.
Идеология 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 реализовано 3 класса для аутентификации пользователя:
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 в данном случае вообще игнорируется.
header('Authorization: Bearer FFFF70it7tzNsHddEiq0BZ0i-OU8S3xV');
Токен — это более безопасный способ авторизации, т.к вы можете задать время жизни такому токену, генерировать его и шифровать надежными криптографическими методами, а так же генерировать уникальный токен при каждом обращении. Правила валидации и проверки токена нужно реализовать в методе findIdentityByAccessToken класса User.
// site?access-token=FFFF70it7tzNsHddEiq0BZ0i-OU8S3xV $behaviors = parent::behaviors(); $behaviors['authenticator']['class'] = QueryParamAuth::className(); $behaviors['authenticator']['tokenParam'] = 'hash'; // site?hash=FFFF70it7tzNsHddEiq0BZ0i-OU8S3xV
$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 ничем не отличается от разграничений прав в обычном приложении. Так как я уже подробно описывала RBAC в Yii2, повторяться не буду.
$behaviors[ 'access'] = [ 'class' => AccessControl::className(), 'rules' => [ [ 'allow' => true, 'roles' => ['@'], ], ], ];
Если остались вопросы — задавайте их в комментариях, а я постараюсь на них ответить.
А как проверять право на действие не с помощью фильтров AccessControl, а с помощью проверки конкретного действия? По типу if(Yii::$app->user->can(‘deleteUser’))?
Вы сами ответили на свой вопрос if(Yii::$app->user->can(«правило_или_разрешение»)). Более того, фильтр AccessControl вызывает в том числе метод matchRole, который использует все тот-же $user->can($role).
Повторюсь, разграничение прав доступа для REST API ничем не отличается от разграничений для обычных приложений. Отличается способ, по которому восстанавливается текущий юзер, тк нет сессии и кук. А все остальное точно так-же как и в обычных приложениях.
какой смысл излагать документацию?
Ммм, какой вообще смысл обсуждать что-либо в блогах и на форумах. Есть же документация 🙂
Я пытаюсь рассказать по-проще чем в документации. И надеюсь это кому-нибудь пригодится. Важность документации при этом ничуть не занижая. Но в Yii пока лучшая документация — это исходники.
Для проверки через user->can в преднаписанных экшенах получается нужно их переопределять и самому проверять правила?
if(Yii::$app->user->can(«правило_или_разрешение»)){
parent::index();
… };
Сам ответил.. Нужно переопределить checkAccess..
public function checkAccess($action, $model = null, $params = [])
{
if($action == ‘index’)
{
if(!Yii::$app->user->can(‘Rule’))
{
throw new \yii\web\HttpException(403);
}
}
…
…
}
Здравствуйте, к сожалению статья не очень понятная, в отличии от предыдущей. Как мне проверять логин и пароль (клиент отправляет в JSON формате) в случае если такой есть отдавать ему его токен?
В статье речь о HTTP-авторизации, т.е об авторизации через HTTP заголовки. Если клиент отправляет логин/пароль в json формате в теле запроса (что совсем не секьюрно), то вам нужен простой action, который будет получать данные, проверять логин/пароль как в стандартом actionLogin, но не логинеть юзера, а возвращать токен, по которому потом можно будет идентифицировать юзера. Дальше клиент будет передавать уже этот токен.
То, что данные в формате Json для Yii вообще не страшно. Пропишите в конфиге компонента request:
И yii сам поймет как распарсить данные в зависимости от сontentType.
А у меня такой момент рест сервис возвращается по ссылка с виндовой авторизацией в всплывающем окне как мне тут авторизоваться ?
«Виндовая» авторизация это Basic авторизация, см HttpBasicAuth. Отправлять base64 (логин:пароль) в http заголовках.
Мне нужно авторизоваться через логи и пароль а потом по токену работать с апи как быть?
Yii позволяет подключить сразу несколько способов авторизации (см CompositeAuth). Затем в зависимости от полученных заголовков управление получит тот или иной класс. К примеру первый запрос с basic авторизацией (логин и пароль, зашифрованные в base64) в ответ API вернет токен. Дальше все запросы с Bearer заголовком с данным токеном.
Есть ли какой-то встроенный механизм для получения токена или его нужно самому писать? если есть, то как найти url? где задается время жизни токена?
Все на усмотрение разработчика. И это правильно: разного уровня проекты нуждаются в разном уровне защиты. Как правило токены генерируются случайным образом. В некоторых системах токены обновляются при каждом запросе, а в некоторых вообще не устаревают. Где-то токен обновится так, что юзер даже этого не заметит, а где-то ему придется снова вводить логин/пароль чтобы получить свежий токен. Все зависит от приложения и его потребностей 🙂
Автору респект за
но тут многие поломают голову, т к пароль не хранится в открытом виде, нужно заменить на:
HttpBasicAuth означает что юзеру будет каждый раз(или только первый?) вываливаться типовое окно авторизации юзер-пароль как при использовании .htpassw или как там его в апаче?..
Нет, HttpBasicAuth это способ программно передать юзер-пароль на страницу с использование basic авторизации. Если вы открываете эту страницу в браузере — то вводите логин-пароль, если программно стучитесь (API) на эту странцу, то логин-пароль нужно передать в заголовках иначе доступ будет запрещен.
Можна ли как то прикрутить Yii::t()
При REST запросе, важно не столько сообщение, сколько сам HTTP код ответа. Если ну очень хочется переопределить сообщение, то нужно:
1) отнаследоваться от класса аутентификации (к примеру HttpBearerAuth) и добавить в него метод
т.е тем самым перекрыть этот метод у родителя класса HttpBearerAuth (родитель: yii\filters\auth\AuthMethod)
2) в контроллере прописать использование своего класса вместо оригинального HttpBearerAuth
А вообще по хорошему, локализация сообщения должна быть на стороне клиента. Rest вернул 401 заголовок — клиент понял что это означает «не авторизован» и вывел пользователю сообщение на том языка, на каком пользователю нужно.
Здравствуйте, что-то прочтя документацию и ваш гайд, я все равно не понял где прописывается header. Можете подробнее описать этот момент?
Эта статья о том, как ограничить доступ к страницам или API по паролю. Для этого не нужно прописывать header-ы. Yii проверит есть ли нужные заголовки в запросе и проверит пароли, прежде чем предоставит доступ к вашему API или странице.
Если вы спрашиваете о том, как в ответе Yii передать заголовки авторазиции (например если ваш API вызывает сторонний сервер и ему нужно убедиться что ответ пришел именно от вас), то примерно так: