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 相关文章推荐
建立文件交换功能的脚本(三)
Oct 09 PHP
php 设计模式之 工厂模式
Dec 19 PHP
超级好用的一个php上传图片类(随机名,缩略图,加水印)
Jun 30 PHP
PHP编码规范之注释和文件结构说明
Jul 09 PHP
php中http_build_query 的一个问题
Mar 25 PHP
PHP爆绝对路径方法收集整理
Sep 17 PHP
PHP获取服务器端信息的方法
Nov 28 PHP
php删除文本文件中重复行的方法
Apr 28 PHP
前端必学之PHP语法基础
Jan 01 PHP
php中让人头疼的浮点数运算分析
Oct 10 PHP
php简单中奖算法(实例)
Aug 15 PHP
关于ThinkPHP中的异常处理详解
May 11 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
php 使用curl模拟ip和来源进行访问的实现方法
2017/05/02 PHP
php检查函数必传参数是否存在的实例详解
2017/08/28 PHP
PHP实现基于PDO扩展连接PostgreSQL对象关系数据库示例
2018/03/31 PHP
由prototype_1.3.1进入javascript殿堂-类的初探
2006/11/06 Javascript
动态载入/删除/更新外部 JavaScript/Css 文件的代码
2010/07/03 Javascript
Javscript调用iframe框架页面中函数的方法
2014/11/01 Javascript
jQuery中unbind()方法用法实例
2015/01/19 Javascript
jQuery判断一个元素是否可见的方法
2015/06/05 Javascript
javascript排序函数实现数字排序
2015/06/26 Javascript
使用OpenLayers3 添加地图鼠标右键菜单
2015/12/29 Javascript
JavaScript中解决多浏览器兼容性23个问题的快速解决方法
2016/05/19 Javascript
javascript将中国数字格式转换成欧式数字格式的简单实例
2016/08/02 Javascript
jQuery中fadein与fadeout方法用法示例
2016/09/16 Javascript
js关于getImageData跨域问题的解决方法
2016/10/14 Javascript
浅谈vuex之mutation和action的基本使用
2017/08/29 Javascript
JS常见构造模式实例对比分析
2018/08/27 Javascript
js canvas实现5张图片合成一张图片
2019/07/15 Javascript
element ui分页多选,翻页记忆的实例
2019/09/03 Javascript
JavaScript将数组转换为链表的方法
2020/02/16 Javascript
如何在面试中手写出javascript节流和防抖函数
2020/10/22 Javascript
python中pandas.DataFrame的简单操作方法(创建、索引、增添与删除)
2017/03/12 Python
Python提取转移文件夹内所有.jpg文件并查看每一帧的方法
2019/06/27 Python
在pycharm中关掉ipython console/PyDev操作
2020/06/09 Python
python实现录制全屏和选择区域录屏功能
2021/02/05 Python
教你使用Canvas处理图片的方法
2017/11/28 HTML / CSS
移动端Html5中百度地图的点击事件
2019/01/31 HTML / CSS
党的群众路线教育实践活动自我剖析材料
2014/10/08 职场文书
免职证明样本
2014/10/23 职场文书
2015年社区居委会工作总结
2015/05/18 职场文书
《鸡兔同笼》教学反思
2016/02/19 职场文书
2019年教师节:送给所有老师的祝福语
2019/09/05 职场文书
创业计划书之游泳馆
2019/09/16 职场文书
聊聊golang中多个defer的执行顺序
2021/05/08 Golang
Node实现搜索框进行模糊查询
2021/06/28 Javascript
javascript Number 与 Math对象的介绍
2021/11/17 Javascript
解决Git推送错误non-fast-forward的方法
2022/06/25 Servers