Авторизация через bearer токен для одного из контроллеров в проекте

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
oklimm
Сообщения: 4
Зарегистрирован: 2022.09.01, 17:46

Авторизация через bearer токен для одного из контроллеров в проекте

Сообщение oklimm »

Здравствуйте.
Есть проект, в котором доступ к большинству страниц дается после стандартной авторизации по логину/паролю.
Пытаюсь добавить в него один контроллер, авторизация в котором работает через bearer токен.
На примере базового приложения это выглядит так:

1) Почти нетронутая модель User

Код: Выделить всё

<?php

namespace app\models;

use yii\base\NotSupportedException;

class User extends \yii\base\BaseObject implements \yii\web\IdentityInterface
{
    public $id;
    public $username;
    public $password;
    public $authKey;

    private static $users = [
        '100' => [
            'id' => '100',
            'username' => 'admin',
            'password' => 'admin',
            'authKey' => 'test100key',
        ],
        '101' => [
            'id' => '101',
            'username' => 'demo',
            'password' => 'demo',
            'authKey' => 'test101key',
        ],
    ];


    /**
     * {@inheritdoc}
     */
    public static function findIdentity($id)
    {
        return isset(self::$users[$id]) ? new static(self::$users[$id]) : null;
    }

    /**
     * {@inheritdoc}
     */
    public static function findIdentityByAccessToken($token, $type = null)
    {
        throw new NotSupportedException('"findIdentityByAccessToken" is not implemented');
    }

    /**
     * Finds user by username
     *
     * @param string $username
     * @return static|null
     */
    public static function findByUsername($username)
    {
        foreach (self::$users as $user) {
            if (strcasecmp($user['username'], $username) === 0) {
                return new static($user);
            }
        }

        return null;
    }

    /**
     * {@inheritdoc}
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * {@inheritdoc}
     */
    public function getAuthKey()
    {
        return $this->authKey;
    }

    /**
     * {@inheritdoc}
     */
    public function validateAuthKey($authKey)
    {
        return $this->authKey === $authKey;
    }

    /**
     * Validates password
     *
     * @param string $password password to validate
     * @return bool if password provided is valid for current user
     */
    public function validatePassword($password)
    {
        return $this->password === $password;
    }
}
2) Модель User2 для доступа по bearer токену

Код: Выделить всё

<?php

namespace app\models;

use yii\base\NotSupportedException;

class User2 extends \yii\base\BaseObject implements \yii\web\IdentityInterface
{
    public $id;
    public $username;
    public $accessToken;

    private static $users = [
        '111' => [
            'id' => '111',
            'username' => 'agent',
            'accessToken' => '111-agent-token',
        ],
    ];


    /**
     * {@inheritdoc}
     */
    public static function findIdentity($id)
    {
        return isset(self::$users[$id]) ? new static(self::$users[$id]) : null;
    }

    /**
     * {@inheritdoc}
     */
    public static function findIdentityByAccessToken($token, $type = null)
    {
        foreach (self::$users as $user) {
            if ($user['accessToken'] === $token) {
                return new static($user);
            }
        }

        return null;
    }

    /**
     * {@inheritdoc}
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * {@inheritdoc}
     */
    public function getAuthKey()
    {
        throw new NotSupportedException('"getAuthKey" is not implemented');
    }

    /**
     * {@inheritdoc}
     */
    public function validateAuthKey($authKey)
    {
        return false;
    }
}
3) В конфигурационном файле web.php добавлены компоненты для обоих классов

Код: Выделить всё

        'user' => [
            'identityClass' => 'app\models\User',
            'enableAutoLogin' => false,
        ],
        'user2' => [
            'class' => 'yii\web\User',
            'identityClass' => 'app\models\User2',
            'enableAutoLogin' => false,
            'enableSession' => false,
        ],
4) Сам контроллер, в котором авторизация должна идти через User2 модель:

Код: Выделить всё

<?php

namespace app\controllers;

use Yii;
use yii\filters\AccessControl;
use yii\filters\auth\HttpBearerAuth;
use yii\rest\Controller;

class SecondController extends Controller
{
    /**
     * {@inheritdoc}
     */
    public function behaviors()
    {
        $behaviors = parent::behaviors();

        $behaviors['authenticator']['authMethods'] = [
            HttpBearerAuth::className(),
        ];

        $behaviors['access'] = [
            'user' => 'user2',
            'class' => AccessControl::class,
            'rules' => [
                [
                    'allow' => true,
                    'roles' => ['@'],
                ],
            ],
        ];

        return $behaviors;
    }

    public function actionIndex()
    {
        return;
    }
}
Вот только при отправке GET запроса на second/index получаю ошибку, из которой слдует, что для поиска пользователя по токену используется модель User, а не User2

Код: Выделить всё

{
    "name": "Not Supported",
    "message": "\"findIdentityByAccessToken\" is not implemented",
    "code": 0,
    "type": "yii\\base\\NotSupportedException",
    "file": "D:\\Apache_htdocs\\basic\\models\\User.php",
    "line": 43,
    "stack-trace": [
        "#0 D:\\Apache_htdocs\\basic\\vendor\\yiisoft\\yii2\\web\\User.php(305): app\\models\\User::findIdentityByAccessToken('111-agent-token', 'yii\\\\filters\\\\aut...')",
        "#1 D:\\Apache_htdocs\\basic\\vendor\\yiisoft\\yii2\\filters\\auth\\HttpHeaderAuth.php(62): yii\\web\\User->loginByAccessToken('111-agent-token', 'yii\\\\filters\\\\aut...')",
        "#2 D:\\Apache_htdocs\\basic\\vendor\\yiisoft\\yii2\\filters\\auth\\CompositeAuth.php(74): yii\\filters\\auth\\HttpHeaderAuth->authenticate(Object(yii\\web\\User), Object(yii\\web\\Request), Object(yii\\web\\Response))",
        "#3 D:\\Apache_htdocs\\basic\\vendor\\yiisoft\\yii2\\filters\\auth\\AuthMethod.php(59): yii\\filters\\auth\\CompositeAuth->authenticate(Object(yii\\web\\User), Object(yii\\web\\Request), Object(yii\\web\\Response))",
        "#4 D:\\Apache_htdocs\\basic\\vendor\\yiisoft\\yii2\\filters\\auth\\CompositeAuth.php(57): yii\\filters\\auth\\AuthMethod->beforeAction(Object(yii\\base\\InlineAction))",
        "#5 D:\\Apache_htdocs\\basic\\vendor\\yiisoft\\yii2\\base\\ActionFilter.php(77): yii\\filters\\auth\\CompositeAuth->beforeAction(Object(yii\\base\\InlineAction))",
        "#6 [internal function]: yii\\base\\ActionFilter->beforeFilter(Object(yii\\base\\ActionEvent))",
        "#7 D:\\Apache_htdocs\\basic\\vendor\\yiisoft\\yii2\\base\\Component.php(633): call_user_func(Array, Object(yii\\base\\ActionEvent))",
        "#8 D:\\Apache_htdocs\\basic\\vendor\\yiisoft\\yii2\\base\\Controller.php(297): yii\\base\\Component->trigger('beforeAction', Object(yii\\base\\ActionEvent))",
        "#9 D:\\Apache_htdocs\\basic\\vendor\\yiisoft\\yii2\\web\\Controller.php(218): yii\\base\\Controller->beforeAction(Object(yii\\base\\InlineAction))",
        "#10 D:\\Apache_htdocs\\basic\\vendor\\yiisoft\\yii2\\base\\Controller.php(176): yii\\web\\Controller->beforeAction(Object(yii\\base\\InlineAction))",
        "#11 D:\\Apache_htdocs\\basic\\vendor\\yiisoft\\yii2\\base\\Module.php(552): yii\\base\\Controller->runAction('index', Array)",
        "#12 D:\\Apache_htdocs\\basic\\vendor\\yiisoft\\yii2\\web\\Application.php(103): yii\\base\\Module->runAction('second/index', Array)",
        "#13 D:\\Apache_htdocs\\basic\\vendor\\yiisoft\\yii2\\base\\Application.php(384): yii\\web\\Application->handleRequest(Object(yii\\web\\Request))",
        "#14 D:\\Apache_htdocs\\basic\\web\\index.php(12): yii\\base\\Application->run()",
        "#15 {main}"
    ]
}
Помогите, пожалуйста, разобраться что не так
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Авторизация через bearer токен для одного из контроллеров в проекте

Сообщение samdark »

Из трейса видно валится на CompositeAuth->authenticate, то есть на authenticator, а до access дело даже не доходит. Поэтому и конфиг ничего не меняет.
oklimm
Сообщения: 4
Зарегистрирован: 2022.09.01, 17:46

Re: Авторизация через bearer токен для одного из контроллеров в проекте

Сообщение oklimm »

Здесь две модели пользователя. Первая - app\models\User - должна аутентифицировать по логину и паролю. Этот класс не поддерживает findIdentityByAccessToken и выкидывает Exception.
Вторая - app\models\User2 - должна аутентифицировать по токену.

Если аутентификация происходит до access фильтра, то как вобще работает настройка user из AccessControl?
Как для конкретного контроллера указать другой класс пользователя для аутентификации?
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Авторизация через bearer токен для одного из контроллеров в проекте

Сообщение samdark »

Настройка user из AccessControl работает для AccessControl. Вам нужно указать ещё https://www.yiiframework.com/doc/api/2. ... ser-detail
Ответить