# 浏览器跨域
协议,端口和主机不是相同的,则跨域。其实主要是用来防止
CSRF
攻击的。简单点说,CSRF
攻击是利用用户的登录态发起恶意请求。
# 图像Ping
与服务器进行简单,单向的跨域通信的方式。请求的数据是通过查询字符串形式发送的,而响应可以是任意内容。
var img = new Image()
img.onload = img.onerror = function() {
alert('Done')
}
img.src = "http://www.example.com/test?name=Nicholas"
2
3
4
5
缺点是只能发送
GET
请求,二是无法访问服务器的响应文本。
# JSONP
能够直接访问响应文本,支持在浏览器与服务器之间双向通信。
function jsonp(url, jsonpCallback, success) {
let script = document.createElement('script')
script.src = url
script.async = true
script.type = 'text/javascript'
window[jsonpCallback] = function(data) {
success && success(data)
}
document.body.appendChild(script)
}
jsonp('http://xxx', 'callback', function(value) {
console.log(value)
})
2
3
4
5
6
7
8
9
10
11
12
13
JSONP
缺点
首先
JSONP
是从其他域中加载代码执行。如果其他域不安全,很有可能会在响应中夹带一些恶意代码,而此时除了完全放弃JSONP
调用之外,没有办法追究。要确定JSONP
请求是否失败也不容易
TIP
JSONP
解决跨域的原理:首先在客户端注册一个callback
, 然后把callback
的名字传给服务器。此时,服务器先生成一个function
, function
名字就是传递上来的参数。最后将 json
数据直接以入参的方式,放置到 function
中,这样就生成了一段 js
语法的文档,返回给客户端。客户端浏览器,解析script
标签,并执行返回的 javascript
文档,此时数据作为参数,传入到了客户端预先定义好的 callback
函数里。
# CORS
服务端设置Access-Control-Allow-Origin
就可以开启CORS
。该属性表示哪些域名可以访问资源,如果设置通配符表示所有网站都可以访问资源。虽然设置CORS
和前端没什么关系,但是通过这种方式解决跨域问题的话,会在发送请求时出现两种情况,分别为简单请求和复杂请求。
- 简单请求
某些请求不会触发
CORS
预检请求,这样的请求一般称为“简单请求”。
- 方法
get post head
Content-Type
仅限:text/plain,multipart/form-data,application/x-www-form-urlencoded
HTTP
头信息不超过Accept Accept-Language Content-Language Last-Evnet-ID Content-Type
- 请求中的任意
XMLHttpRequestUpload
对象没有注册任何事件监听器 - 请求中没有使用
ReadableStream
对象
- 复杂请求
对于复杂请求来说,首先会发起一个预检请求,该请求是
options
方法的,返回码为204
。通过该请求来知道服务端是否允许跨域请求。
跨域共享标准规范要求,对那些可能对服务器数据产生副作用的
HTTP
请求方法(特别是GET
以外的HTTP
请求,或者搭配某些MIME
类型的POST
请求),浏览器必须首先使用OPTIONS
方法发起一个预检请求(preflight request
),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的HTTP
请求。
WARNING
由此可见,当触发预检时,跨域请求便会发送 2
次请求,既增加了请求数,也延迟了请求真正发起的时间,严重影响性能。
- 优化
Options
请求- 转为简单请求,如用
JSONP
做跨域请求 - 对
options
请求进行缓存,服务器端设置Access-Control-Max-Age
字段,那么当第一次请求该URL
时会发出OPTIONS
请求,浏览器会根据返回的Access-Control-Max-Age
字段缓存该请求的OPTIONS
预检请求的响应结果(具体缓存时间还取决于浏览器的支持的默认最大值,取两者最小值,一般为10
分钟)。在缓存有效期内,该资源的请求(URL
和header
字段都相同的情况下)不会再触发预检。(chrome
打开控制台可以看到,当服务器响应Access-Control-Max-Age
时只有第一次请求会有预检,后面不会了。注意要开启缓存,去掉disable cache
勾选。)
- 转为简单请求,如用
TIP
关于cors
的cookie
问题
想要传递cookie
满足3个条件:
web
请求设置withCredentials
默认情况下跨域请求浏览器不带
cookie
。所以需要设置
Access-Control-Allow-Credentials
为true
Access-Control-Allow-Origin
为非*
# document.domain
该方式只能用于二级域名相同的情况下,比如
a.test.com
和b.test.com
。只需要给页面添加document.domain = 'test.com'
表示二级域名相同就可以实现跨域。
// a.test.com
<body>
helloa
<iframe
src="http://b.test.com/b.html"
frameborder="0"
onload="load()"
id="frame"
></iframe>
<script>
document.domain = "test.com";
function load() {
console.log(frame.contentWindow.a);
}
</script>
</body>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// b.test.com
<body>
hellob
<script>
document.domain = "test.com";
var a = 100;
</script>
</body>
2
3
4
5
6
7
8
# devServer
vue项目(node的正向代理)
跨域是浏览器禁止的,服务端并不禁止跨域,所以浏览器可以发给自己的服务端然后由自己的服务端再转发给要跨域的服务端,做一层代理。
vue-cli
的proxy
用的就是http-proxy-middleware
中间件,http-proxy-middleware
内部用http-proxy
TIP
正向代理,意思是一个位于客户端和原始服务器(origin server
)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。
# nginx
反向代理
TIP
反向代理(Reverse Proxy
)方式是指以代理服务器来接受internet
上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet
上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。
# 两个浏览器窗口之间通信
localStorage
localstorge
在一个标签页里被添加、修改或删除时,都会触发一个storage
事件,通过在另一个标签页里监听storage
事件,即可得到localstorge
存储的值,实现不同标签页之间的通信。
WebSocket
所有的
WebSocket
都监听同一个服务器地址,利用send
发送消息,利用onmessage
获取信息的变化,不仅能不同窗口,还能跨浏览器,兼容性最佳,只是需要消耗服务器资源。这种方式本质没有使用HTTP
,因此也没有跨域限制
let socket = new WebSocket('ws://localhost:3000');
socket.open = function () {
socket.send('hello');
}
socket.onmessage = function(e) {
console.log(e.data); // 接收服务器返回的数据
}
2
3
4
5
6
7
postMessage
借助iframe
或window.open
otherWindow.postMessage(message, targetOrigin, [transfer]);
otherWindow
其他窗口的一个引用,比如
iframe
的contentWindow
属性、执行window.open
返回的窗口对象、或者是命名过或数值索引的window.frames
。
message
将要发送到其他
window
的数据。它将会被结构化克隆算法序列化。这意味着你可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化。
targetOrigin
通过窗口的
origin
属性来指定哪些窗口能接收到消息事件,其值可以是字符串""(表示无限制)或者一个URI
。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin
提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口;例如,当用postMessage
传送密码时,这个参数就显得尤为重要,必须保证它的值与这条包含密码的信息的预期接受者的origin
属性完全一致,来防止密码被恶意的第三方截获。如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的targetOrigin
,而不是不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。
transfer
可选
是一串和
message
同时传递的Transferable
对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。
TIP
window.postMessage()
方法可以安全地实现跨源通信。通常,对于两个不同页面的脚本,只有当执行它们的页面位于具有相同的协议(通常为https
),端口号(443
为https
的默认值),以及主机 (两个页面的模数Document.domain
设置为相同的值) 时,这两个脚本才能相互通信。可以在
http
返回头 添加X-Frame-Options: SAMEORIGIN
防止被别人添加至iframe
。
# 即时通信技术
Comet
- 服务器发送事件
SSE
Web Socket