在 web 开发中, 前端向后端发送请求, 基本上都是用 Ajax 的方式. 如果我们前端页面的 url 和我们要提交的后端 url 存在跨域问题时, 我们该如何解决呢?
下面将分别讨论几种解决方案.
1.1 CORS 解决跨域
CORS 是一套解决前后端跨域通信的解决方案, 简单说是一种前后端用于允许跨域通信的一种约定机制. 下图 1 简单明了简述了 CORS 的概念.
图 1 CORS 通信示意图
只要浏览器和后端做好相关的对接和支持工作, CORS 就能跑通.
目前除 IE10 以下的 IE 浏览器, 其余主流浏览器均支持 CORS.
服务器端, 只需要设置特定的头就可以允许跨域通信:
- // 允许 milo.qq.com 的请求跨域
- header("Access-Control-Allow-Origin:milo.qq.com");
- // 设置通配符, 允许所有请求跨域
- header("Access-Control-Allow-Origin:*");
有一点需要注意: 不建议后端配置 Access-Control-Allow-Origin 头为通配符 *, 因为为了安全起见, 这种设置不可控.
建议后端以白名单的形式加 header 头, 对于白名单内的请求, 设置对应的跨域头, 否则拒绝跨域.
可以参考:
- // 配置允许跨域请求的白名单 origin
- $allowed_origin= array(
- 'http://a.qq.com',
- 'http://b.qq.com',
- 'http://www.qq.com'
- );
- // 获取本次请求的 origin
- $origin=isset($_SERVER['HTTP_ORIGIN'])?$_SERVER['HTTP_ORIGIN']:'';
- // 判断是否白名单
- if(in_array($origin,$allowed_origin)){
- // 设置允许跨域头
- header("Access-Control-Allow-Origin:'.$origin);
- }
1.2 跨域发送 cookie
上面说到了 CORS 可以跨域, 但是我们发现简单得设置了 Access-Control-Allow-Origin 头并不能把 cookie 带过去, cookie 作为前后端通信中常用的数据载体经常用于校验凭证等数据传输, 非常重要.
比如在 a.qq.com 的网站上, 请求了一个 c.qq.com/xxx.PHP 的接口, 但是此接口需要从 c.qq.com 的域名下拿 cookie 中的登录态作为身份校验, 这时发现 cookie 取不到.
下面简单介绍一下通过 CORS 实现跨域发送 cookie.
设置 withCredentials 相关头
跨域发送 cookie 只需要前端带上 withCredentials 相关头, 并且后端加上 Access-Control-Allow-Credentials:true 即可.
具体操作如下:
[前端代码]
- // 当前位于 a.qq.com 中, 向 c.qq.com/xx.PHP 接口发送请求
- $.Ajax({
- type:'GET',
- url:'http://c.qq.com/xx.php',
- xhrFields:{
- withCredentials:true
- },
- success:function(res){
- console.log(res);
- },
- fail:function(){
- }
- })
[后端代码]
- // 当前为 c.qq.com/xx.PHP
- // 设置为制定的 origin, 不能设置为 *
- header('Access-Control-Allow-Origin:http://a.qq.com');
- // 允许携带 cookie
- header('Access-Control-Allow-Credentials:true');
[需要注意:]
跨域发送 cookie, 后端设置 Access-Control-Allow-Origin 头不能设置为通配符, 否则浏览器将会拒绝跨域并报错.
1.3 JSONP 解决跨域
JSONP 本质上是 script 请求, 是前端页面中用于外链 script 的一种请求方式. 由于 script 标签有天然的跨域特性 (拥有此特性的还用 img 标签等), 而且其返回的内容为文本, 且可以直接执行的特点. 故通过将请求返回的内容封装成 JS 脚本的形式, 在前端直接执行的方式可以得到后端返回内容.
使用 JSONP 跨域请求后端可以这么做:
[前端代码]
- // 以 jQuery 调用为例
- $.Ajax({
- url:'http://c.qq.com/xx.php',
- dataType:'jsonp',// 表示返回格式为 JSONP
- type:'GET',
- success:function(res){
- console.log(res);
- },
- fail:function(){
- }
- })
前端调用默认会发出一个类似:
http://c.qq.com/xx.php?callback=xxxxxx 的请求到后端, 后端拿到 callback 参数值, 后会将其作为回调方法, 直接返回一段用 callback 调用 responseData 的方法即可.
[后端代码]
- $callbackName=$_GET['callback'] || 'callback';
- $retData=array(
- "a" =>1,
- "b" =>2
- );
- echo $callbackName.'('.json_encode($retData).')';
[优点与缺点]
使用 JSONP 方式跨域的优点很明显, 就是兼容性强, 所有浏览器均支持. 而且后端改造的成本也低.
缺点就是 JSONP 本质上是 script 请求, 只能支持 GET 请求, 对于大数据量和传输文件等都不支持, 而且也无法拿到相关的返回头, 状态码等数据.
来源: https://www.qcloud.com/developer/article/1358957