Yii2:前端和 API 之间的同时认证

Yii2: Simultaneous authentication between frontend and API

我已经将后端变成了 API。 API 控制器具有 HttpBasicAuth 类型的身份验证。

问题是,即使在前端进行身份验证后,每当向 API 发出请求时,都会出现身份验证 window。

当用户在前端进行身份验证时,在向 API 发出请求时不会再次请求访问的用户名和密码,我该怎么做?

控制器示例 API:

class CategoryController extends ActiveController
{
    public $modelClass = 'api\models\Category';

    public function behaviors()
    {
        $behaviors = parent::behaviors();

        $behaviors['authenticator'] = [
            'class' => CompositeAuth::className(),
            'authMethods' => [
                [
                    'class' => HttpBasicAuth::className(),
                    'auth' => function($username, $password) {
                        $out = null;
                        $user = \common\models\User::findByUsername($username);
                        if ($user != null) {
                            if ($user->validatePassword($password)) $out = $user;
                        }
                        return $out;
                    }
                ],
            ],
        ];
        return $behaviors;
    }
}

这叫分享session。它还取决于您的层应用程序(前端和 api)是否都在同一个域中。如果是,请配置您的前端和 api 设置(<app>/frontend/config/main.php<app>/api/config/main.php),如下所示:

'components' => [
    ...
    'request' => [
        'csrfParam' => '_csrf-shared',
    ],
    ...
    'user' => [
        'identityClass' => 'common\models\User',
        'enableAutoLogin' => true,
        'identityCookie' => ['name' => '_identity-shared', 'httpOnly' => true],
    ],
    ...
    'session' => [
        'name' => 'advanced-shared',
    ],
    ...

意思是你保存的cookies和session同名,这样当你在前端登录,然后去backend/api,后端获取相同的cookies,所以你会检测为经过身份验证的用户。

这里有一个重要提示,为了 enableAutoLogin 对两层都起作用,您应该为两个 main-local.php 设置设置相同的 cookieValidationKey。您可以手动设置它们,或编辑 init.php 文件为所有层生成一个 cookieValidationKey。 (只要确保你知道自己在做什么)。

顺便说一句,我认为在frontendapi 之间同时进行身份验证不是一个好主意。如果是 frontendbackend 那么它仍然可以忍受,但是 api 交互与 frontend 相比是不同的。
我建议像 Authorization: Bearer <token> 一样使用 headers。您可以在此处获得更多信息 Yii2 Rest Authentication

更新

我想这就是你需要的。在 common/components 文件夹中创建一个 class,即 ApiAuth 并粘贴以下代码:

<?php

namespace common\components;

use yii\filters\auth\HttpBasicAuth;

class ApiAuth extends HttpBasicAuth
{
    /**
     * @inheritdoc
     */
    public function authenticate($user, $request, $response)
    {
        if ($user->identity) {
            return $user->identity;
        }

        return parent::authenticate($user, $request, $response);
    }
}

此 class 从 yii\filters\auth\HttpBasicAuth 扩展而来。在调用浏览器提示之前,它会检查 user->dentity 是否已填充。如果是这样,则不需要提示。

在您的控制器行为中,将 HttpBasicAuth 替换为 ApiAuth class:

use common\components\ApiAuth;
...
'authMethods' => [
    [
        'class' => ApiAuth::className(),
        'auth' => function($username, $password) {
        ...

因为用户已经通过身份验证,所以我只为连接的用户设置 "AccessControl"。如果他们没有连接,他们将收到代码 403 而不是 401。

这解决了问题:

class CategoryController extends ActiveController
{
    public $modelClass = 'api\models\Category';

    public function behaviors()
    {
        $behaviors = parent::behaviors();

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

        return $behaviors;
    }
}