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上传文件时文件过大$_FILES为空的解决方法
Nov 26 PHP
PHP中4个加速、缓存扩展的区别和选用建议
Mar 12 PHP
非常好用的Zend Framework分页类
Jun 25 PHP
PHP数组操作类实例
Jul 11 PHP
phpinfo()中Loaded Configuration File(none)的解决方法
Jan 16 PHP
老生常谈php 正则中的i,m,s,x,e分别表示什么
Mar 02 PHP
PHP开发中解决并发问题的几种实现方法分析
Nov 13 PHP
PHP有序表查找之二分查找(折半查找)算法示例
Feb 09 PHP
PHP基于timestamp和nonce实现的防止重放攻击方案分析
Jul 26 PHP
php和html的区别点详细总结
Sep 24 PHP
php文件上传原理与实现方法详解
Dec 20 PHP
gearman中任务的优先级和返回状态实例分析
Feb 27 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
Win9x/ME下Apache+PHP安装配置
2006/10/09 PHP
用户的详细注册和判断
2006/10/09 PHP
计算2000年01月01日起到指定日的天数
2006/10/09 PHP
关于Appserv无法打开localhost问题的解决方法
2009/10/16 PHP
PHP中使用php://input处理相同name值的表单数据
2015/02/03 PHP
php编译安装php-amq扩展简明教程
2016/06/25 PHP
jQuery 1.0.4 - New Wave Javascript(js源文件)
2007/01/15 Javascript
document.getElementById获取控件对象为空的解决方法
2013/11/20 Javascript
table行随鼠标移动变色示例
2014/05/07 Javascript
DOM节点删除函数removeChild()用法实例
2015/01/12 Javascript
JS文字球状放大效果代码分享
2015/08/19 Javascript
JS实现不使用图片仿Windows右键菜单效果代码
2015/10/22 Javascript
JS实现将Asp.Net的DateTime Json类型转换为标准时间的方法
2016/08/02 Javascript
微信小程序 保留小数(toFixed)详细介绍
2016/11/16 Javascript
Angular.JS判断复选框checkbox是否选中并实时显示
2016/11/30 Javascript
javascript 正则表达式去空行方法
2017/01/24 Javascript
react中fetch之cors跨域请求的实现方法
2018/03/14 Javascript
layer弹出层父子页面事件相互调用方法
2018/08/17 Javascript
微信小程序与后台PHP交互的方法实例分析
2018/12/10 Javascript
详解关于html,css,js三者的加载顺序问题
2019/04/10 Javascript
layui 中select下拉change事件失效的解决方法
2019/09/20 Javascript
VUE实现密码验证与提示功能
2019/10/18 Javascript
python完成FizzBuzzWhizz问题(拉勾网面试题)示例
2014/05/05 Python
编写Python脚本来获取Google搜索结果的示例
2015/05/04 Python
Python2.7.10以上pip更新及其他包的安装教程
2018/06/12 Python
selenium+python实现自动化登录的方法
2018/09/04 Python
python机器学习实现决策树
2019/11/11 Python
pandas和spark dataframe互相转换实例详解
2020/02/18 Python
Python ORM框架Peewee用法详解
2020/04/29 Python
HTML5 LocalStorage 本地存储刷新值还在
2017/03/10 HTML / CSS
详解HTML5常用的语义化标签
2019/09/27 HTML / CSS
四年大学生活的个人自我评价
2013/12/11 职场文书
餐厅总厨求职信
2014/03/04 职场文书
临床专业自荐信
2014/06/22 职场文书
Golang 链表的学习和使用
2022/04/19 Golang
bose降噪耳机音能消除人声吗
2022/04/19 数码科技