最近在做一个项目,打算前后端分离,前端用的是vue-element-admin,后台用的是php的yii框架。结果就碰到了跨域问题,如下所示:
说到跨域,我们就要讲一下为什么会有跨域问题,其根本是浏览器的同源策略。
同源策略
同源策略(Same Origin Policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略的基础之上的,浏览器只是针对同源策略的一种实现。
出于安全性,浏览器限制脚本内发起的跨源HTTP请求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。
浏览器的同源策略,限制了来自不同源的“document”或脚本,对当前“document”读取或设置某些属性。
这一策略极其重要,试想如果没有同源策略,可能a.com的一段JavaScript脚本,在b.com未曾加载此脚本时,也可以随意涂改b.com的页面(在浏览器的显示中)。为了不让浏览器的页面行为发生混乱,浏览器提出了“Origin”(源)这一概念,来自不同Origin的对象无法互相干扰。
对于JavaScript来说,以下情况被认为是同源与不同源的。
浏览器中JavaScript的同源策略(当JavaScript被浏览器认为来自不同源时,请求被拒绝)由上表可以看出,影响“源”的因素有:host(域名或IP地址,如果是IP地址则看做一个根域名)、子域名、端口、协议。
换言之,a.com通过以下代码:
加载了b.com上的b.js,但是b.js是运行在a.com页面中的,因此对于当前打开的页面(a.com页面)来说,b.js的Origin就应该是a.com而非b.com。
在浏览器中,、<img>、<iframe>、<link>等标签都可以跨域加载资源,而不受同源策略的限制。这些带“src”属性的标签每次加载时,实际上是由浏览器发起了一次GET请求。不同于XMLHttpRequest的是,通过src属性加载的资源,浏览器限制了JavaScript的权限,使其不能读、写返回的内容。
但是互联网是开放的,随着业务的发展,跨域请求的需求越来越迫切,因此W3C委员会制定了XMLHttpRequest跨域访问标准。它需要通过目标域返回的HTTP头来授权是否允许跨域访问,因为HTTP头对于JavaScript来说一般是无法控制的,所以认为这个方案可以实施,这种方案最终通过跨源资源共享(CORS)机制来实现。
跨源资源共享
跨源域资源共享机制允许 Web 应用服务器进行跨源访问控制,从而使跨源数据传输得以安全进行。现代浏览器支持在 API 容器中使用 CORS,以降低跨源 HTTP 请求所带来的风险。
跨源资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是GET以外的 HTTP 请求,或者搭配某些 MIME 类型的POST请求),浏览器必须首先使用 OPTIONS方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨源请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括Cookies和 HTTP 认证相关数据)。
主要的三个HTTP响应首部字段
1:Access-Control-Allow-Origin
Access-Control-Allow-Origin
响应头指定了该响应的资源是否被允许与给定的origin共享。
如需允许所有资源都可以访问您
的资源,您可以如此设置:
Access-Control-Allow-Origin:*
如需允许https://developer.mozilla.org
访问您的资源,您可以设置:
Access-Control-Allow-Origin: https://developer.mozilla.org
2:Access-Control-Allow-Methods
响应首部 Access-Control-Allow-Methods
在对 preflight request.(预检请求)的应答中明确了客户端所要访问的资源允许使用的方法或方法列表。
假如只允许GET,POST方式,您可以如此设置:
Access-Control-Allow-Methods: GET,POST
如果允许所有请求方法,那么可以如此设置
Access-Control-Allow-Methods: *
3:Access-Control-Allow-Headers
响应首部 Access-Control-Allow-Headers 用于 preflight request (预检请求)中,列出了将会在正式请求的 Access-Control-Request-Headers 字段中出现的首部信息。
简单首部,如 simple headers、Accept、Accept-Language、Content-Language、Content-Type (只限于解析后的值为 application/x-www-form-urlencoded、multipart/form-data 或 text/plain 三种MIME类型(不包括参数)),它们始终是被支持的,不需要在这个首部特意列出。
如果请求中含有 Access-Control-Request-Headers 字段,那么这个首部是必要的。
如果没有特殊要求,一般直接设置成:
Access-Control-Allow-Headers:*
解决方法:
那么如何解决一开始说的跨域问题呢?我们在yii的入口文件加上这三行就可以了
header('Access-Control-Allow-Origin:*'); header('Access-Control-Allow-Methods:*'); header("Access-Control-Allow-Headers:*");
在这里我全部设置成了*,只是为了配合本地环境测试,但这是不提倡的做法,线上环境千万不要这样设置,需要按照实际的情况来。
参考资料:
- https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
- https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
- https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
- https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS
- 《白帽子讲web安全》