【问题标题】:Yii2 CORS with Auth not working for non CRUD actions带有身份验证的 Yii2 CORS 不适用于非 CRUD 操作
【发布时间】:2020-07-14 00:22:05
【问题描述】:

我正在 Yii2 中构建一个 API,并添加了 CORS 和身份验证。这适用于所有创建/读取/更新/删除操作,但不适用于自定义操作。有没有人经历过这种情况?

网址管理器:

['class' => 'yii\rest\UrlRule', 'controller' => 'api/v1/user', 'pluralize' => false],

控制器行为:

public function behaviors()
{
    return ArrayHelper::merge([
            'corsFilter' => [
                'class' => Cors::className(),
            ],
            [
                'class' => HttpBearerAuth::className(),
                'except' => ['options',
                             'login',
                ],
            ],
        ], parent::behaviors()
    );
}

如前所述,CRUD 的操作很好,但自定义操作(例如 http://domain.com/user/test)将响应 401 Unauthorised 响应。

难道不能让 CORS 和 auth 在自定义操作上协同工作吗?

编辑:我应该补充一点,问题 (401) 仅在浏览器发出 OPTIONS 请求时发生。正常请求(curl、Postman)不受影响。 RESTful、Cors、Auth 组合似乎会出现此问题。

【问题讨论】:

  • 浏览器控制台中的完整错误消息是什么?
  • 还有你在扩展什么控制器 \yii\rest\ActiveController\yii\rest\Controller ?您是否覆盖或更改了内置 options 操作的工作方式?
  • @salem,我正在扩展 ActiveController。
  • @Salem,浏览器控制台显示:XMLHttpRequest cannot load http://poly.api/api/v1/user/me. Invalid HTTP status code 401

标签: php rest yii2 cors


【解决方案1】:

试试这个:

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

    unset($behaviors['authenticator']);

    $behaviors['corsFilter'] = [
        'class' => Cors::className(),
        'cors' => [
            'Origin' => ['*'],
            'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
            'Access-Control-Request-Headers' => ['*'],
            'Access-Control-Allow-Credentials' => true,
        ],
    ];

    $behaviors['authenticator'] = [
        'class' =>  HttpBearerAuth::className(),
        'except' => ['options','login'],
    ];

    return $behaviors;
}

它将取消设置parent controller 实现的默认authenticator,以确保首先处理cors。然后我们强制cors 允许凭据,然后再实施您自己的authenticator


可能引发 Unauthorized 错误的另一件事是未找到或错误的Options 响应,因为浏览器首先请求它获取允许的动词列表。您可以在浏览器的网络选项卡中查看其标题响应中的列表。

一般规则是,当您要求浏览器对任何 url 执行诸如 PUT、DELETE 或 POST 之类的合理动词时,它可能首先向同一 url 发送 OPTIONS 请求>(检查this)在发送真正的请求之前检查该动词是否被允许。所以 Yii 应该被配置为通过执行正确的重定向来响应所有这些 OPTIONS 动词。

ActiveController 实现的默认 CRUD 操作正在使用那些 default patterns

'PUT,PATCH {id}' => 'update',
'DELETE {id}' => 'delete',
'GET,HEAD {id}' => 'view',
'POST' => 'create',
'GET,HEAD' => 'index',
'{id}' => 'options',
'' => 'options',

因此,无论您在urlManager['rules'] 中实现了什么配置,请确保不要覆盖最后两个配置,如果您使用自定义模式,请始终记住包含其等效的options 动词,如下例所示:

[
    'class' => 'yii\rest\UrlRule', 
    'controller' => ['account' => 'auth/account'], 
    'patterns' => [
        'POST,HEAD login'  => 'login',
        'POST,HEAD signup' => 'signup',
        'POST req-reset-pass' => 'request-password-reset',
        'POST reset-pass' => 'reset-password',
        // OPTTIONS VERBS
        'OPTIONS login' => 'options',
        'OPTIONS signup' => 'options',
        'OPTIONS req-reset-pass' => 'options',
        'OPTIONS reset-pass' => 'options',
    ]
],

extraPatterns 中添加自定义模式时也是如此。


Options 操作默认在ActiveController 中实现。它的代码可以看到here。 如果您要扩展与 ActiveController 不同的控制器,例如 \yii\rest\Controller,请务必手动包含它:

public function actions() 
{
    $actions = parent::actions();
    $actions['options'] = [
        'class' => 'yii\rest\OptionsAction',
        // optional:
        'collectionOptions' => ['GET', 'POST', 'HEAD', 'OPTIONS'],
        'resourceOptions' => ['GET', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
    ];
    return $actions;
}

【讨论】:

  • 已修复。非常感谢您的彻底回复。这让我非常痛苦。它将模式声明添加到修复它的 URL 管理器。我永远感激不尽。
  • 我刚刚发现,当我将patterns 属性添加到 urlManager 时,它已经破坏了update 功能(例如PUT user/1)。现在,当我尝试更新时,我得到一个404 response。删除模式属性可以纠正这个问题。我似乎找不到解决方案。有什么想法吗?
  • 我通过添加另一个模式 'PUT,PATCH {id}' => 'update', 解决了上面直接提到的问题。
【解决方案2】:

只需将您的 corsFilter 块移动到验证器块上方,如下所示:

public function behaviors()
    {
        return [
            'corsFilter' => [
                'class' => \yii\filters\Cors::className(),
            ],
            'authenticator' => [
                'class' => HttpBearerAuth::className(),
            ],
            'contentNegotiator' => [
                'class' => ContentNegotiator::className(),
                'formats' => [
                    'application/json' => Response::FORMAT_JSON,
                ],
            ],

        ];
    }

【讨论】:

    猜你喜欢
    • 2013-06-09
    • 1970-01-01
    • 2016-08-04
    • 1970-01-01
    • 1970-01-01
    • 2018-06-10
    • 2018-01-24
    • 1970-01-01
    • 2017-01-30
    相关资源
    最近更新 更多