1. 什么是跨域?
因为浏览器出于安全考虑,存在同源策略。所谓同源是指域名,协议,端口均相同,也就是说,如果协议、域名或者端口有一个不同就是跨域。跨域是指一个域的网页去请求另一个域的资源。这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如客户端与服务端跨域通信,或者跨页面通信。浏览器执行javascript脚本时,会检查这个脚本属于哪个页面,如果不是同源页面,就不会被执行。
- 跨域问题并不是请求发不出去。请求能发出去,服务端能收到请求并正常返回结果,只是浏览器认为不安全,所以把响应结果拦截了
(我本人几乎没用过跨页面通信,所以下面仅对客户端与服务间端跨域通信的方式进行简单介绍)
2. 解决跨域问题的方法----客户端与服务端跨域通信
(1). 图像ping(单向)
- 什么是图像ping:
图像ping是与服务器进行简单、单向的跨域通信的一种方式,请求的数据是通过查询字符串的形式发送的,而响应可以是任意内容,但通常是像素图或204响应(No Content)。 通过图像ping,浏览器得不到任何服务器的响应文本,都只能通过侦听load和error事件,知道响应是什么时候接收到的。图像ping有两个主要缺点:首先就是只能发送get请求,其次就是无法访问服务器的响应文本。因此图像ping只能进行单向通信。 - 简单使用
var img = new Image(); img.onload = img.onerror = function(){ alert("done!"); }; img.src = "https://www.example.com/test?name='tom'";
- 与< img>类似的可以跨域内嵌资源的还有:
(1)< script src="">< /script>标签嵌入跨域脚本。语法错误信息只能在同源脚本中捕捉到。
(2) < link src="">标签嵌入CSS。
(3)< video> 和 < audio>嵌入多媒体资源。
(4)< object>, < embed> 和< applet>的插件。
(5)@font-face引入的字体。一些浏览器允许跨域字体( cross-origin fonts),一些需要同源字体(same-origin fonts)。
(6) < frame> 和 < iframe>载入的任何资源。站点可以使用X-Frame-Options消息头来阻止这种形式的跨域交互。
(2). 通过jsonp跨域
JSONP是JSON with Padding(填充式json)的简写,是应用JSON的一种新方法,只不过是被包含在函数调用中的JSON,例如:
callback({"name","trigkit4"});
JSONP由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数,而数据就是传入回调函数中的JSON数据。
【< script>标签与< img>标签都有能力不受限制的从其他域加载资源】
JSONP的原理:通过< script>标签来使用的,使用时通过src属性指定一个跨域url,在url参数中指定的回调函数,传送给服务端,然后服务端生成一段js代码且代码中包含着对回调函数的调用,然后服务端把这段js代码返回给客户端,客户端浏览器获取到这段js并能自动执行,在执行这段代码的过程中调用了回调函数,然后你就可以在回调函数里面处理返回数据了。
<script type="text/javascript">
function dosomething(jsondata){
//处理获得的json数据
}
</script>
<script src="http://example.com/data.php?callback=dosomething"></script>
- JSONP的优缺点:
- JSONP的优点是:它不受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行;在请求完毕后可以通过回调函数获取响应数据。
- JSONP的缺点则是:它只支持GET请求;它不安全,可能会遭受XSS攻击。
(3). 跨域资源共享(CORS)
CORS(Cross-Origin Resource Sharing)跨域资源共享。
CORS的基本思想就是使用HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。
浏览器将CORS跨域请求分为简单请求和非简单请求。只要同时满足以下两个条件,就属于简单请求:
(1)使用下列方法之一:
- head
- get
- post
(2)HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- 有Content-Type 但字段的类型不是application/json
简单请求
对于简单请求,客户端不需要进行处理,在发送请求时,浏览器会自动附加一个额外的Origin请求头部,其中包含请求页面的源信息(协议、域名和端口)。以便服务器根据这个头部信息来决定是否给予响应。如果服务器认为请求可以接受。就会设置Access-Control-Allow-Origin的响应头字段,来允许可以访问的域进行跨域操作。
// *表示允许所有域访问或指定详细源信息(协议、域名和端口)
'Access-Control-Allow-Origin: * ’非简单请求
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。非简单请求会在正式通信之前首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。设置以 Access-Control-开头的一系列响应头字段,来允许跨域操作:
'Access-Control-Allow-Origin: * ’
'Access-Control-Allow-Methods:* ’
…
CORS和JSONP对比
(CORS与JSONP相比,无疑更为先进、方便和可靠。)
- JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
- 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。
- JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS)。
(4). nginx反向代理跨域
实现思路:通过nginx配置一个代理服务器做跳板机,反向代理要访问的另一个域的服务器,隐藏真实的服务器。
使用nginx反向代理实现跨域,是最简单的跨域方式。只需要修改nginx的配置即可解决跨域问题,支持所有浏览器,支持session,不需要修改任何代码,并且不会影响服务器性能。
先下载nginx,然后将nginx目录下的nginx.conf修改配置:
server {
#监听80端口,不加端口号时默认为80端口
listen 80;
#访问域名为www.123.com
server_name www.123.com;
location / {
proxy_pass http://127.0.0.1:8080;
# 该指令用于设置网站的默认首页。
index index.html index.htm index.jsp;
}
}
最后通过命令行nginx -s reload启动nginx
(5). webpack的devServer.proxy处理跨域
vue在vue.config.js(新建)配置重写webpack,修改devServer 配置proxy实现跨域。原理大致与 nginx 相同。通过配置proxy在本地虚拟一个代理服务器并代理转发请求,隐藏真实的服务器,实现数据的转发
#代理服务器
module.exports = {
devServer:{
host:'localhost',
port:8080,
proxy:{
'/api':{
//真实服务器地址
target:'http://122.51.238.153',
//在本地虚拟一个代理服务器并代理转发请求
changeOrigin:true,
pathRewrite:{
// 请求时,会进行替换
'^/api':''
}
}
}
}
}