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 相关文章推荐
PHP运行时强制显示出错信息的代码
Apr 20 PHP
php日期转时间戳,指定日期转换成时间戳
Jul 17 PHP
解析PHPExcel使用的常用说明以及把PHPExcel整合进CI框架的介绍
Jun 24 PHP
PHP生成网站桌面快捷方式代码分享
Oct 11 PHP
完美实现wordpress禁止文章修订和自动保存的方法
Nov 03 PHP
PHP提示Warning:phpinfo() has been disabled函数禁用的解决方法
Dec 17 PHP
PHP的serialize序列化数据以及JSON格式化数据分析
Oct 10 PHP
php二维码生成
Oct 19 PHP
老生常谈php 正则中的i,m,s,x,e分别表示什么
Mar 02 PHP
PHP微信PC二维码登陆的实现思路
Jul 13 PHP
ThinkPHP框架获取最后一次执行SQL语句及变量调试简单操作示例
Jun 13 PHP
PHP htmlspecialchars() 函数实例代码及用法大全
Sep 18 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
《Re:从零开始的异世界生活 冰结之绊》
2020/04/09 日漫
symfony表单与页面实现技巧
2015/01/26 PHP
php基于socket实现SMTP发送邮件的方法
2015/03/05 PHP
PHP实现支持加盐的图片加密解密
2016/09/09 PHP
详谈PHP中的密码安全性Password Hashing
2017/02/04 PHP
PHP的new static和new self的区别与使用
2019/11/27 PHP
PHP并发场景的三种解决方案代码实例
2021/02/27 PHP
window.onbeforeunload方法在IE下无法正常工作的解决办法
2010/01/23 Javascript
jq实现酷炫的鼠标经过图片翻滚效果
2014/03/12 Javascript
Javascript 赋值机制详解
2014/11/23 Javascript
jQuery实现鼠标划过展示大图的方法
2015/03/09 Javascript
动态的9*9乘法表效果的实现代码
2016/05/16 Javascript
JS获取子窗口中返回的数据实现方法
2016/05/28 Javascript
JS获取鼠标相对位置的方法
2016/09/20 Javascript
常用js,css文件统一加载方法(推荐) 并在加载之后调用回调函数
2016/09/23 Javascript
javascript循环链表之约瑟夫环的实现方法
2017/01/16 Javascript
JavaScript中使用Async实现异步控制
2017/08/15 Javascript
Bootstrap Table 删除和批量删除
2017/09/22 Javascript
JS实现提交表单前的数字及邮箱校检功能
2017/11/13 Javascript
vue2.0使用v-for循环制作多级嵌套菜单栏
2018/06/25 Javascript
对angularjs框架下controller间的传值方法详解
2018/10/08 Javascript
python关于矩阵重复赋值覆盖问题的解决方法
2019/07/19 Python
python模块和包的应用BASE_PATH使用解析
2019/12/14 Python
python中round函数如何使用
2020/06/19 Python
Python编写万花尺图案实例
2021/01/03 Python
css3实现一款模仿iphone样式的注册表单
2013/03/20 HTML / CSS
用纯css3实现的图片放大镜特效效果非常不错
2014/09/02 HTML / CSS
飞利浦比利时官方网站:Philips比利时
2016/08/24 全球购物
英国电气世界:Electrical World
2019/09/08 全球购物
乡村教师党员四风问题对照检查材料思想汇报
2014/10/08 职场文书
自我检讨书范文
2015/01/28 职场文书
后天观后感
2015/06/08 职场文书
2016年幼儿园庆六一开幕词
2016/03/04 职场文书
浅谈 JavaScript 沙箱Sandbox
2021/11/02 Javascript
利用Python将list列表写入文件并读取的方法汇总
2022/03/25 Python
js前端面试常见浏览器缓存强缓存及协商缓存实例
2022/06/21 Javascript