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函数解决SQL injection
Dec 09 PHP
php读取文件内容的三种可行方法示例介绍
Feb 08 PHP
php循环创建目录示例分享(php创建多级目录)
Mar 04 PHP
五款PHP代码重构工具推荐
Oct 14 PHP
ThinkPHP分组下自定义标签库实例
Nov 01 PHP
php获取当月最后一天函数分享
Feb 02 PHP
php实现的网络相册图片防盗链完美破解方法
Jul 01 PHP
php等比例缩放图片及剪切图片代码分享
Feb 13 PHP
php获取服务器操作系统相关信息的方法
Oct 08 PHP
Thinkphp5结合layer弹窗定制操作结果页面
Jul 07 PHP
laravel框架上传图片实现实时预览功能
Oct 14 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
建立动态的WML站点(一)
2006/10/09 PHP
php实现快速排序法函数代码
2012/08/27 PHP
Laravel学习教程之IOC容器的介绍与用例
2017/08/15 PHP
PHP结合Ffmpeg快速搭建流媒体服务的实践记录
2018/10/31 PHP
laravel框架邮箱认证实现方法详解
2019/11/22 PHP
一个基于jquery的图片切换效果
2010/07/06 Javascript
js+css在交互上的应用
2010/07/18 Javascript
学习javascript,实现插入排序实现代码
2011/07/31 Javascript
Javascript中的this绑定介绍
2011/09/22 Javascript
jQuery实现连续动画效果实例分析
2015/10/09 Javascript
jQuery如何使用自动触发事件trigger
2015/11/29 Javascript
JavaScript跨域调用基于JSON的RESTful API
2016/07/09 Javascript
用js控件div的滚动条,让它在内容更新时自动滚到底部的实现方法
2016/10/27 Javascript
jQuery实现Table表格隔行变色及高亮显示当前选择行效果示例
2017/02/14 Javascript
微信、QQ、微博、Safari中使用js唤起App
2018/01/24 Javascript
JavaScript代码实现txt文件的上传预览功能
2018/03/27 Javascript
python使用正则表达式提取网页URL的方法
2015/05/26 Python
python查看zip包中文件及大小的方法
2015/07/09 Python
python基本语法练习实例
2017/09/19 Python
Python随机生成身份证号码及校验功能
2018/12/04 Python
pyqt5 键盘监听按下enter 就登陆的实例
2019/06/25 Python
Python 调用 Outlook 发送邮件过程解析
2019/08/08 Python
春节到了 教你使用python来抢票回家
2020/01/06 Python
Python urllib2运行过程原理解析
2020/06/04 Python
解决keras加入lambda层时shape的问题
2020/06/11 Python
python实现二分类和多分类的ROC曲线教程
2020/06/15 Python
Django如何继承AbstractUser扩展字段
2020/11/27 Python
python用分数表示矩阵的方法实例
2021/01/11 Python
解决CSS3的opacity属性带来的层叠顺序问题
2016/05/09 HTML / CSS
人力资源管理专业自荐书范文
2014/02/10 职场文书
业务员简历自我评价
2014/03/06 职场文书
施工质量承诺书范文
2014/05/30 职场文书
研究生求职自荐书
2014/06/23 职场文书
高中信息技术教学反思
2016/02/16 职场文书
python之django路由和视图案例教程
2021/07/26 Python
深入讲解数据库中Decimal类型的使用以及实现方法
2022/02/15 MySQL