Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[js] 第40天 为什么会有跨域问题?怎么解决跨域? #150

Open
haizhilin2013 opened this issue May 25, 2019 · 9 comments
Open
Labels
js JavaScript

Comments

@haizhilin2013
Copy link
Collaborator

第40天 为什么会有跨域问题?怎么解决跨域?

@haizhilin2013 haizhilin2013 added the js JavaScript label May 25, 2019
@rocky-191
Copy link

浏览器为了安全,产生了同源策略,协议、域名、端口有一个不同,就会产生跨域。跨域方式有jsonp,代理方式,cors,domain改变主域相同,postmessage也可以

@AnsonZnl
Copy link
Contributor

在使用Vue搭建的一个后端管理系统中,我使用axios请求本地的Node环境下的接口,但是请求失败,然后我错误信息是:
331552554043_.pic.jpg
大概意思就是不能访问http://localhost:8080
我的Vue项目端口是http://localhost:8081,Node服务端运行在http://localhost:8080端口上,也就是说因为请求端口和响应端口不一致,所以请求失败。
我也在网上查看了一些关于跨域出现的原因及解决的方法,并记录下来。

为什么会有跨域

跨域一句话的理解就是:服务端和请求端的地址不一样。

什么是跨域

Ajax 的便利性大家都清楚,可以在不向服务器提交完整的页面的情况下,实现局部更新页面。但是浏览器处于对安全方面的考虑,不允许跨域调用其他页面的对象。
其实这个也不能怪浏览器,假设谁都可以随随便便向你发送请求,那样有很大的安全隐患。
根据浏览器的同源策略, 只有当协议,域名,端口相同的时候才算是同源, 反之则均视为是一个跨域的请求.
也就是说我刚刚的Vue端口是8081,服务端端口是8080,端口不一样,因为同源策略的存在 ,所有我的请求会失败。

一个问题,当找到了原因,这个问题就解决了一半了。

怎么解决跨域

下面就先介绍三种跨全域的方法:

JSONP

应该是最常见解决跨域的方法了,
他为什么能解决跨域呢,是因为Web 页面上调用 js 文件不受浏览器同源策略的影响,所以通过 Script 便签可以进行跨域的请求:

  1. 首先前端先设置好回调函数,并将其作为 url 的参数。
  2. 服务端接收到请求后,通过该参数获得回调函数名,并将数据放在参数中将其返回
  3. 收到结果后因为是 script 标签,所以浏览器会当做是3脚本进行运行,从而达到跨域获取数据的目的。
    我的前端是index.html,后端是server.js
    后端逻辑:
//server.js
const url = require('url');
const http = require('http');

http.createServer((req, res)=>{
const data = {
    x: 10//返回的数据
};
const callback = url.parse(req.url, true).query.callback;
res.writeHead(200);
res.end(`${callback}(${JSON.stringify(data)})`);
//执行回调函数,返回data
}).listen(3000, 'localhost');

console.log('启动服务,监听 localhost:3000');

然后使用node server.js运行
前端:

//index.html
<body>
    <script>
	function jsonpCallback(data) {
	    console.log('获得 X 数据:' + data.x);
	}
    </script>
    <script src="http://localhost:3000?callback=jsonpCallback"></script>
</body>

之后打开index.html;就可以在控制台看到返回的数据了:
341552556856_.pic.jpg

至此,通过 JSONP 跨域获取数据已经成功了,jsonp这种方法跨域,他的兼容性很好,可以在古老的浏览器中国使用,因为这种方法是利用了<script>标签的特殊性,所有只支持GET请求。

CORS

CORS 是一个 W3C 标准,全称是"跨域资源共享"(Cross-origin resource sharing)它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 ajax 只能同源使用的限制。

CORS 需要浏览器和服务器同时支持才可以生效,对于开发者来说,CORS 通信与同源的 ajax 通信没有差别,代码完全一样。浏览器一旦发现 ajax 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨源通信。

前端:

<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script>
$.ajax({
    url:"http://127.0.0.1:3000",
    success:function(res){
        var res = JSON.parse(res);
        $('body').text(res.data);
        console.log(res.data);
    }
});
</script>

这次前端启动需要使用node-server来启动,使用npm install node-server下载,然后当前目录下使用node-server就可以了
后端:

const http = require('http');

http.createServer((req, res)=>{
const data = {
    'data': 'Hello world'//返回的数据
};
res.writeHead(200, {'Access-Control-Allow-Origin': 'http://127.0.0.1:8080'});
//设置的头部信息需要和前端请求的地址一致
res.end(JSON.stringify(data));
//返回data
}).listen(3000, '127.0.0.1');

console.log('启动服务,监听 127.0.0.1:3000');

使用命令node server.js启动;
211552638161_.pic_hd.jpg

CORS与JSONP的使用目的相同,但是比JSONP更强大。

JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。

Server Proxy

服务器代理,顾名思义,当你需要有跨域的请求操作时发送请求给后端,让后端帮你代为请求,然后最后将获取的结果发送给你。

假设有这样的一个场景,你的页面需要获取 CNode:Node.js专业中文社区 论坛上一些数据,如通过 https://cnodejs.org/api/v1/topics,当时因为不同域,所以你可以将请求后端,让其对该请求代为转发。

后端代码如下:

const url = require('url');
const http = require('http');
const https = require('https');

http.createServer((req, res)=>{
const path = url.parse(req.url).path.slice(1);
//核对请求路由是否一致
if(path === 'topics'){
    https.get('https://cnodejs.org/api/v1/topics', (resp)=>{
        //https代发请求
        let data='';
        resp.on('data', chunk=>{
            data+= chunk
        });
        resp.on('end', ()=>{
            res.writeHead(
                200,
                {'Content-Type': 'application/json; charset=utf-8'}
            );
            res.end(data);
            //返回数据
        })
    })
}

}).listen(3000, '127.0.0.1');

console.log('启动服务,监听 127.0.0.1:3000');

前端代码:

<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script>
$.ajax({
    url:"https://cnodejs.org/api/v1/topics",
    success:function(res){
        $('body').text(JSON.stringify(res));
        console.log(res);
    }
});
</script>

这样就成功了
221552639459_.pic_hd.jpg

总结

常用的跨域方式基本就是这三种:

  1. JSONP
    优点是可以兼容老浏览器,缺点是只能发送GET请求
  2. CORS
    优点简单方便,支持post请求,缺点是需要后端的配合,不支持老版浏览器。。
  3. Server Proxy
    优点是前端正常发送ajax请求,缺点是后端会二次请求。

其他的跨域方式还有:location.hashwindow.namepostMessage等方式,有时间也可以了解一下。

参考资料:

@tzjoke
Copy link

tzjoke commented May 28, 2019

为什么会有跨域?因为同源策略
怎么解决跨域?JSONP兼容老浏览器,只读API,CORS简单,读写API。
说几个CORS实际遇到的问题,在使用fetch的时候content-type也有要求,比如 text/xml 就会失败;另外要带cookie,得设置请求with credentials=true,服务器也有一个allow credentials 的属性也要设置,并且allow origin不能为 *

@myprelude
Copy link

  • 为啥会有跨域问题
    浏览器的同源策略;什么是不同源:不同协议、不同域名、不同端口
  • 怎么解决跨域呢?
    方案一
    JSONP:通过script可以跨域的原理,执行服务端的回调函数
    方案二
    代理:nigix 或者webpack 代理 配置
    方案三
    CORS :"跨域资源共享",设置'Access-Control-Allow-Origin=*

@xuxihua
Copy link

xuxihua commented Jan 9, 2020

前端常见跨域解决方案(全):https://segmentfault.com/a/1190000011145364

@Alex-Li2018
Copy link

跨域解决方案:

  1. jsonp
  2. cros: allow-control-origin
  3. nginx
  4. service proxy (webpack devService)
  5. postMessage
  6. websocket

@MrZ2019
Copy link

MrZ2019 commented Oct 12, 2020

浏览器为了安全,产生了同源策略,协议、域名、端口有一个不同,就会产生跨域。跨域方式有jsonp,代理方式,cors,domain改变主域相同,postmessage也可以

@MrZ2019
Copy link

MrZ2019 commented Oct 30, 2020

  • 为啥会有跨域问题
    浏览器的同源策略;什么是不同源:不同协议、不同域名、不同端口
  • 怎么解决跨域呢?
    方案一
    JSONP:通过script可以跨域的原理,执行服务端的回调函数
    方案二
    代理:nigix 或者webpack 代理 配置
    方案三
    CORS :"跨域资源共享",设置'Access-Control-Allow-Origin=*

@xiaoqiangz
Copy link

为什么会产生跨域? 因为浏览器的同源策略,不同协议、不同域名、不同端口会产生跨域。
如何解决跨域:
1 JsonP: 因为Img、script标签是可以跨域请求的,利用这个特点,配置回调函数,然后服务端去执行该函数
2 nginx反向代理
3 服务端设置CROS
4 vue项目可以利用proxyTable来处理跨域,其原理是起了个node服务器,利用服务器之间不会跨域的原理,进行交互。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
js JavaScript
Projects
None yet
Development

No branches or pull requests

9 participants