针对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的跨域请求流程:
实测成功
【关键在于】