跨域通信

什么是同源策略及限制

如果两个uri的协议, 域名, 端口三者完全相同,就称之为同源。
同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互
这是一个用于隔离潜在恶意文件的关键安全机制:

  1. cookie,localstorage,indexdb无法读取
  2. dom无法获得
  3. ajax请求不能发送

跨域通信的几种方式

  • JSONP: callback后面的参数是函数,返回的字段是函数的参数,原理src的元素不会被同源策略拦截(img, link, script这些元素)
1
2
3
4
5
6
<!-- 不会被拦截,请求发送出去 -->
<!-- 比如服务端返回add(1, 2) -->
<script>
const add = (a, b) => { console.log('a and b', a, b, a + b)}
</script>
<script src="http://localhost:2000/api/todo/all/jsonp?callback=add&param1=10&param2=20" />
1
2
3
4
5
6
app.get('/api/todo/all/jsonp', (request, response) => {
let query = { reqeust }
let {callback, param1, param2} = { query }
let s = `${callback}(${param1}, ${param2})`
response.send(s)
})
  • Hash: hash改变页面不刷新
1
2
3
4
5
6
// 场景: 当前页面a通过iframe嵌入了页面b
let b = document.querySelector('iframe')
b.src = b.src + '#' + 'json.stringify(data)'
window.onhashchange = function() {
let data = window.location.hash
}
  • postMessage: 窗口A向窗口B发送信息
1
2
3
4
5
6
Bwindow.postMessage('data', 'http://B.com')
Awindow.addEventListener('message', function(event){
log(event.origin) // http://A.com
log(event.source) // Bwindow
log(event.data) // data
})
  • WebSocket
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var ws = new WebSocket('wss://localhost:8080');

ws.onopen = function(evt) {
console.log("Connection open ...");
ws.send("Hello WebSockets!");
};

ws.onmessage = function(evt) {
console.log( "Received Message: " + evt.data);
ws.close();
};

ws.onclose = function(evt) {
console.log("Connection closed.");
};
  • CORS
1
2
3
4
5
6
7
const Koa = require('koa')
const cors = require('@koa/cors')

const app = new Koa()
app.use(cors({
origin: '*'
}))
  • nginx配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
server {
listen 9000;
server_name localhost;

#charset koi8-r;
access_log /var/log/nginx/host.access.log main;
error_log /var/log/nginx/error.log error;

location / {
root /usr/share/nginx/html;
index index.html index.html;
}

location /api/ {
rewrite /api/(.*) /$1 break;
proxy_pass http://49.234.21.254:8562;
}

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

在跨域通讯的CORS方式中浏览器做了什么处理

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request) 然后它会给请求报文和响应报文的头部添加一些字段

  • 简单请求, 同时满足以下两大条件

    • 请求方法是以下三种方法之一

      • HEAD
      • GET
      • POST
    • HTTP的头信息不超出以下几种字段

      • Accept

      • Accept-Language

      • Content-Language

      • Last-Event-ID

      • Content-Type:只限于三个值application/x-www-form-urlencodedmultipart/form-datatext/plain

简单请求的相关头部字段

  • 请求报文的头部字段
1
Origin: http://api.bob.com
  • 响应报文的头部字段
1
2
3
4
5
6
7
Access-Control-Allow-Origin: http://api.bob.com
# 表示是否允许发送Cookie
Access-Control-Allow-Credentials: true
# XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:
# Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。
# 如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定
Access-Control-Expose-Headers: FooBar

非简单请求的相关头部字段

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUTDELETE,或者Content-Type字段的类型是application/json

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求
关于预检请求:
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

预检请求的相关字段

  • 请求报文的头部字段
1
2
3
4
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
# 指定浏览器CORS请求会额外发送的头信息字段
Access-Control-Request-Headers: X-Custom-Header
  • 响应报文的头部字段
1
2
3
4
5
Access-Control-Allow-Origin: http://api.bob.com
# 表明服务器支持的所有跨域请求的方法
Access-Control-Allow-Methods: GET, POST, PUT
# 表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段
Access-Control-Allow-Headers: X-Custom-Header
文章作者: woyao
文章链接: https://chenwoyao.github.io/2021/04/23/前端笔记/javascript系列/跨域通信/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 woyao的博客