对Yii2中开启`authenticator`后出现的跨域问题-修复

针对Yii2中开启authenticator后出现的跨域问题,以下是优化后的解决方案。主要修复点包括正确处理OPTIONS预检请求修复认证器配置优化CORS设置

php 复制代码
<?php
namespace app\controllers;

use app\models\SystemLog;
use Yii;
use yii\filters\Cors;
use yii\rest\Controller;
use yii\filters\auth\HttpBearerAuth;
use yii\web\UnauthorizedHttpException;

class ApiController extends Controller
{
    // 日志级别常量
    const LOG_LEVEL_INFO = 'info';
    const LOG_LEVEL_WARNING = 'warning';
    const LOG_LEVEL_ERROR = 'error';

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

        // 移除默认的认证器
        unset($behaviors['authenticator']);

        // CORS 优先 - 修复关键点:允许Authorization头
        $behaviors['corsFilter'] = [
            'class' => Cors::class,
            'cors' => [
                'Origin' => Yii::$app->params['allowedOrigins'] ?? ['http://localhost:3002', 'http://api.mfe.local'],
                'Access-Control-Allow-Credentials' => true,
                'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
                'Access-Control-Request-Headers' => ['*'], // 允许所有头
                'Access-Control-Expose-Headers' => ['*'],  // 暴露所有头给客户端
                'Access-Control-Max-Age' => 86400,
            ],
        ];

        // 认证器配置 - 修复关键点:正确排除OPTIONS
        $behaviors['authenticator'] = [
            'class' => HttpBearerAuth::class,
            'except' => array_merge(['options'], $this->authOptional()),
        ];

        // 日志过滤器(最后执行)
        $behaviors['requestLogger'] = new class extends \yii\base\ActionFilter {
            public function beforeAction($action)
            {
                if (!Yii::$app->request->isOptions) {
                    Yii::info([
                        'action' => $action->id,
                        'request' => Yii::$app->request->rawBody,
                        'headers' => Yii::$app->request->headers->toArray(),
                    ], 'api_requests');
                }
                return parent::beforeAction($action);
            }
        ];

        return $behaviors;
    }

    // 确保OPTIONS方法被排除 - 修复关键点
    protected function authOptional()
    {
        return ['options'];
    }

    // 处理OPTIONS请求 - 新增方法
    public function actionOptions()
    {
        Yii::$app->response->statusCode = 200;
        return [];
    }

    // 认证失败处理 - 跳过OPTIONS请求
    public function beforeAction($action)
    {
        if (!parent::beforeAction($action)) {
            return false;
        }

        // 关键修复:跳过OPTIONS请求的认证检查
        if (Yii::$app->request->isOptions) {
            return true;
        }

        if (Yii::$app->user->isGuest && !in_array($action->id, $this->authOptional())) {
            throw new UnauthorizedHttpException('登录已过期,请重新登录');
        }

        return true;
    }

    /* 其余方法保持不变(getId, getWid, afterAction, success, error等) */
    // ...
}

关键修复说明:

  1. CORS配置优化

    php 复制代码
    'Access-Control-Request-Headers' => ['*'],     // 允许所有请求头
    'Access-Control-Expose-Headers' => ['*'],      // 暴露所有响应头

    确保浏览器能传递和处理Authorization

  2. 认证器配置修复

    php 复制代码
    unset($behaviors['authenticator']); // 移除默认认证器
    $behaviors['authenticator'] = [
        'except' => array_merge(['options'], $this->authOptional()),
    ];

    显式排除OPTIONS请求,避免预检请求被拦截

  3. OPTIONS请求处理

    php 复制代码
    public function actionOptions()
    {
        Yii::$app->response->statusCode = 200;
        return [];
    }

    添加专门的OPTIONS处理器返回空响应

  4. 认证检查跳过OPTIONS

    php 复制代码
    public function beforeAction($action)
    {
        // ...
        if (Yii::$app->request->isOptions) {
            return true; // 直接放行OPTIONS请求
        }
        // ...
    }
  5. 跨域白名单扩展 (可选):

    在配置文件中增加更多允许的源:

    php 复制代码
    // config/params.php
    return [
        'allowedOrigins' => [
            'http://localhost:3002',
            'http://api.mfe.local',
            'https://your-production-domain.com'
        ],
    ];

补充建议:

  1. Nginx/Apache配置

    确保服务器配置允许跨域头:

    nginx 复制代码
    # Nginx配置示例
    add_header 'Access-Control-Allow-Origin' $http_origin always;
    add_header 'Access-Control-Allow-Credentials' 'true' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' '*' always;
  2. 前端Axios配置

    确保请求携带凭证:

    javascript 复制代码
    // 前端请求配置
    axios.defaults.withCredentials = true;
  3. 测试预检请求

    使用curl测试OPTIONS请求:

    bash 复制代码
    curl -X OPTIONS http://api.mfe.local/api/user/info \
      -H "Origin: http://localhost:3002" \
      -H "Access-Control-Request-Method: GET" \
      -H "Access-Control-Request-Headers: authorization" \
      -I

这些修改确保:

  1. OPTIONS预检请求正确处理
  2. Authorization头能被跨域传递
  3. 认证中间件不会拦截预检请求
  4. 服务器返回正确的CORS响应头

修复后,带Bearer Token的跨域请求流程:
浏览器 API服务器 OPTIONS预检请求 跳过认证检查 返回CORS头(200) 实际请求(GET/POST...) 检查Bearer Token 返回数据(200) 返回401错误 alt [认证成功] [认证失败] 浏览器 API服务器

实测成功

【关键在于】

相关推荐
杰哥技术分享16 天前
Yii2 安装-yii2-imagine
开发语言·yii
杰哥技术分享1 个月前
yii2基础版本安装记录,实录有点乱看标题即可
composer·yii
星释1 个月前
Yii2项目自动向GitLab上报Bug
前端·gitlab·bug·yii
菜鸟、小高2 个月前
Yii2.0 模型规则(rules)详解
php·yii
我是唐青枫9 个月前
Yii2 init 初始化脚本分析
php·yii
大爱无疆的疯子1 年前
Yii 结合MPDF 给PDF文件添加多行水印
pdf·php·yii
huaweichenai1 年前
Yii实现RabbitMQ队列
分布式·rabbitmq·php·yii
卢卡上学1 年前
PHP - Yii2 异步队列
php·yii·队列
zzoood2 年前
【Yii2】使用Cache缓存提高性能
缓存·php·yii