对Yii2中开启`authenticator`后出现的跨域问题-修复
针对Yii2中开启authenticator
后出现的跨域问题,以下是优化后的解决方案。主要修复点包括正确处理OPTIONS预检请求、修复认证器配置和优化CORS设置:
<?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等) */// ...
}
关键修复说明:
-
CORS配置优化:
'Access-Control-Request-Headers' => ['*'], // 允许所有请求头 'Access-Control-Expose-Headers' => ['*'], // 暴露所有响应头
确保浏览器能传递和处理
Authorization
头 -
认证器配置修复:
unset($behaviors['authenticator']); // 移除默认认证器 $behaviors['authenticator'] = ['except' => array_merge(['options'], $this->authOptional()), ];
显式排除OPTIONS请求,避免预检请求被拦截
-
OPTIONS请求处理:
public function actionOptions() {Yii::$app->response->statusCode = 200;return []; }
添加专门的OPTIONS处理器返回空响应
-
认证检查跳过OPTIONS:
public function beforeAction($action) {// ...if (Yii::$app->request->isOptions) {return true; // 直接放行OPTIONS请求}// ... }
-
跨域白名单扩展(可选):
在配置文件中增加更多允许的源:// config/params.php return ['allowedOrigins' => ['http://localhost:3002','http://api.mfe.local','https://your-production-domain.com'], ];
补充建议:
-
Nginx/Apache配置:
确保服务器配置允许跨域头:# 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;
-
前端Axios配置:
确保请求携带凭证:// 前端请求配置 axios.defaults.withCredentials = true;
-
测试预检请求:
使用curl测试OPTIONS请求: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
这些修改确保:
- OPTIONS预检请求正确处理
- Authorization头能被跨域传递
- 认证中间件不会拦截预检请求
- 服务器返回正确的CORS响应头
修复后,带Bearer Token的跨域请求流程:
实测成功
【关键在于】