Yii支持多域名cors原理的实现


Posted in PHP onDecember 05, 2018

平常我们遇到跨域问题时,常使用 cors(Cross-origin resource sharin)方式解决。不知你是否注意到,在设置响应头 Access-Control-Allow-Origin 域的值时,只允许设置一个域名,这意味着不能同时设置多个域名来共享资源。而在 Yii2 中直接使用'Origin' => ['http://www.site1.com', 'http://www.site2.com']的形式却可以设置多个 cors 域名值,Why?

Yii支持多域名cors原理的实现

其实,Yii2 中采用了动态设置 Access-Control-Allow-Origin 域值的方法来解决这个问题。

说明:测试使用的接口域名api.d.fanhaobai.com,cros 多域名为www.d.yii.comwww.fq.yii.com

Nginx设置多域名

尝试直接通过 Nginx 的add_header模块追加 Access-Control-Allow-Origin 值实现,如下:

add_header Access-Control-Allow-Origin http://www.fq.yii.com;
add_header Access-Control-Allow-Origin http://www.d.yii.com;

接口 请求 和 响应头 如下:

Response Headers
Access-Control-Allow-Origin: http://www.fq.yii.com
Access-Control-Allow-Origin: http://www.d.yii.com
Connection: keep-alive
Content-Type: application/json; charset=UTF-8
... ...

Request Headers
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8
Host: api.d.fanhaobai.com
Origin: http://www.fq.yii.com
Proxy-Connection: keep-alive
... ...

当前域为www.fq.yii.com,需跨域请求http://api.d.fanhaobai.com/v1/config/list.json的资源。浏览器抛出如下跨域错误:

XMLHttpRequest cannot load http://api.d.fanhaobai.com/v1/config/list.json. The 'Access-Control-Allow-Origin' header contains multiple values 'http://www.fq.yii.com, http://www.d.yii.com', but only one is allowed. Origin 'http://www.fq.yii.com' is therefore not allowed access.

以上信息明确说明,Access-Control-Allow-Origin 只能设置为一个值,即每次请求只能对应一个域名值。故通过该方法不能设置多域名进行 cors。

Yii2设置多域名

Yii2 设置多域名 cors,只需在对应控制器(ConfigController)中设置 cors 行为,如下:

class BaseController extends Controller
{
  /**
   * @inheritdoc
   */
  public function behaviors()
  {
    return [
      'corsFilter' => [
        'class' => \yii\filters\Cors::className(),
        'cors' => [
          //运行cors域名列表
          'Origin' => ['http://www.d.yii.com', 'http://www.fq.yii.com'],
          'Access-Control-Allow-Credentials' => true,
        ]
      ],
    ];
  }
}

重新在www.fq.yii.com发送 cors 请求,发现此时已经不存在跨域问题。响应头 如下:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://www.fq.yii.com
Connection: keep-alive
Content-Type: application/json; charset=UTF-8
... ...

我们会发现,Access-Control-Allow-Origin 域的值为http://www.fq.yii.com,刚好为当前域名一致,且只有一个值,并未出现设置的http://www.d.yii.com值。

同时,在www.d.yii.com下发送 cors 请求,也不存在跨域问题。响应头中 Access-Control-Allow-Origin 值为http://www.d.yii.com

由此可知,Yii2 在控制器行为中设置 Origin 项,只是一个域名白名单,而返回的 Access-Control-Allow-Origin 同请求的域名一致且在这个白名单中,这个 Access-Control-Allow-Origin 由 Yii2 根据当前请求所在域名进行了动态处理。

Yii2动态Access-Control-Allow-Origin

查看 Yii2 的\yii\filters\Cors类源码,如下:

class Cors extends ActionFilter
{
  /**
   * @var array CORS所用的响应头
   */
  public $cors = [
    'Origin' => ['*'],
    'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
    'Access-Control-Request-Headers' => ['*'],
    'Access-Control-Allow-Credentials' => null,
    'Access-Control-Max-Age' => 86400,
    'Access-Control-Expose-Headers' => [],
  ];
  
  /**
   * 执行action前要做的事
   * @inheritdoc
   */
  public function beforeAction($action)
  {
    $this->request = $this->request ?: Yii::$app->getRequest();
    $this->response = $this->response ?: Yii::$app->getResponse();
    ... ...
    $requestCorsHeaders = $this->extractHeaders();
    //获取cors所用的响应头
    $responseCorsHeaders = $this->prepareHeaders($requestCorsHeaders);
    //设置cors所用的响应头
    $this->addCorsHeaders($this->response, $responseCorsHeaders);
    return true;
  }
  
  /**
   * 处理cors所用的响应头,动态处理Access-Control-Allow-Origin域
   * @param array $requestHeaders CORS headers we have detected
   * @return array CORS headers ready to be sent
   */
  public function prepareHeaders($requestHeaders)
  {
    $responseHeaders = [];
    //$requestHeaders['Origin']为源地址,请求所在域名
    if (isset($requestHeaders['Origin'], $this->cors['Origin'])) {
      //源地址在白名单中,则设置Access-Control-Allow-Origin为源地址
      if (in_array('*', $this->cors['Origin']) || in_array($requestHeaders['Origin'], $this->cors['Origin'])) {
        $responseHeaders['Access-Control-Allow-Origin'] = $requestHeaders['Origin'];
      }
    }
    ... ...
   }
}

主要思想就是,查看源地址是否在 cors 白名单中,在则设置 Access-Control-Allow-Origin 域的值为源地址。这样就能满足 Access-Control-Allow-Origin 为一个值的限制,同时也能允许指定的域名进行 cors。

注意:使用该方法请确保 Nginx 配置中未操作 Access-Control-Allow-Origin 域。

总结

通过 Nginx 设置 Access-Control-Allow-Origin 进行 cors,有且只能有一个特定域名,局限性较大。通过代码逻辑操作 Access-Control-Allow-Origin 来实现 cors,则比较灵活,能解决多个域名进行 cors 的需求,但是如果接口异常,跨域设置则会失效。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
一个简单的MySQL数据浏览器
Oct 09 PHP
PHP获取短链接跳转后的真实地址和响应头信息的方法
Jul 25 PHP
PHP中shuffle数组值随便排序函数用法
Nov 21 PHP
浅析php原型模式
Nov 25 PHP
详解Window7 下开发php扩展
Dec 31 PHP
Yii隐藏URL中index.php的方法
Jul 12 PHP
PHP读MYSQL中文乱码的快速解决方法
Oct 01 PHP
浅谈PHP的反射API
Feb 26 PHP
Ubuntu彻底删除PHP7.0的方法
Jul 27 PHP
php闭包中使用use声明变量的作用域实例分析
Aug 09 PHP
解决Laravel无法使用COOKIE和SESSION的问题
Oct 16 PHP
PHP实现本地图片转base64格式并上传
May 29 PHP
php判断电子邮件是否正确方法
Dec 04 #PHP
浅谈Laravel核心解读之Console内核
Dec 02 #PHP
Laravel使用scout集成elasticsearch做全文搜索的实现方法
Nov 30 #PHP
用Laravel Sms实现laravel短信验证码的发送的实现
Nov 29 #PHP
php实现每日签到功能
Nov 29 #PHP
PHP序列化的四种实现方法与横向对比
Nov 29 #PHP
PHP中如何使用Redis接管文件存储Session详解
Nov 28 #PHP
You might like
了解Joomla 这款来自国外的php网站管理系统
2010/03/11 PHP
解析PHP生成静态html文件的三种方法
2013/06/18 PHP
教你如何解密 “ PHP 神盾解密工具 ”
2014/06/20 PHP
codeigniter上传图片不能正确识别图片类型问题解决方法
2014/07/25 PHP
php+ajax实时刷新简单实例
2015/02/25 PHP
浅析Yii2 GridView实现下拉搜索教程
2016/04/22 PHP
制作个性化的WordPress登陆界面的实例教程
2016/05/21 PHP
PHP中error_reporting函数用法详细介绍
2017/06/11 PHP
jQuery实现div浮动层跟随页面滚动效果
2014/02/11 Javascript
js数组中如何随机取出一个值
2014/06/13 Javascript
使用VS开发 Node.js指南
2015/01/06 Javascript
jQuery中extend函数的实现原理详解
2015/02/03 Javascript
AngularJs Managing Service Dependencies详解
2016/09/02 Javascript
微信小程序 获取相册照片实例详解
2016/11/16 Javascript
Vue 2.X的状态管理vuex记录详解
2017/03/23 Javascript
微信小程序自动客服功能
2017/11/02 Javascript
vue 通过下拉框组件学习vue中的父子通讯
2017/12/19 Javascript
简单分析js中的this的原理
2019/08/31 Javascript
微信小程序 checkbox使用实例解析
2019/09/09 Javascript
Node.js API详解之 tty功能与用法实例分析
2020/04/27 Javascript
js根据后缀判断文件文件类型的代码
2020/05/09 Javascript
浅谈JavaScript中this的指向更改
2020/07/28 Javascript
举例讲解Linux系统下Python调用系统Shell的方法
2015/11/07 Python
python用装饰器自动注册Tornado路由详解
2017/02/14 Python
史泰博(Staples)中国官方网站:办公用品一站式采购
2016/09/05 全球购物
Groupon比利时官方网站:特卖和网上购物高达-70%
2019/08/09 全球购物
幼儿教师研修感言
2014/02/12 职场文书
食品业务员岗位职责
2014/03/18 职场文书
党的群众路线教育实践活动心得体会(乡镇)
2014/11/03 职场文书
2014年副班长工作总结
2014/12/10 职场文书
公司行政主管岗位职责
2015/04/09 职场文书
男方家长婚礼致辞
2015/07/27 职场文书
2019学校请假条格式及范文
2019/06/25 职场文书
python使用tkinter实现透明窗体上绘制随机出现的小球(实例代码)
2021/05/17 Python
还在手动盖楼抽奖?教你用Python实现自动评论盖楼抽奖(一)
2021/06/07 Python
小程序自定义轮播图圆点组件
2022/06/25 Javascript