浏览器跨域问题

957 阅读4分钟

同源策略:两个 URL 的 协议(protocol)、端口(port) (如果有指定的话)和 域名(host) 都相同的话,则这两个 URL 是同源。

1、JSONP(json with padding 填充式 json)

原理:利用了 html 标签使用 src 引用静态资源时不受跨域限制的机制, 请求类型:仅支持 get 请求类型 请求格式:${baseUrl}?key1=value1&key2=value2&callback=yourfunc

<script>
    var script = document.createElement('script');
    script.type = 'text/javascript';

    // 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
    script.src = `${baseUrl}?key1=value1&key2=value2&callback=yourfunc`;
    document.head.appendChild(script);

    // 回调执行函数
    function handleCallback(res) {
        alert(JSON.stringify(res));
    }
 </script>

2、iframe 通信

发消息:window.postMessage(message)

接受消息:window.addEventListener('message', receiveMessage, false)

hash 传值

1)document.domain + iframe 跨域
限制:仅限主域相同,子域不同的跨域应用场景
父页面 a.html 嵌入 iframe ,可将该页面和 iframe 标签 src 子页面的 document.domain 设置为相同。获取参数时用 window.parent 获取父页面定义在 window 的全局参数
2)location.hash + iframe 跨域
通过location.hash单向通信
3)postMessage + iframe 跨域

3、CORS(Cross-origin resource sharing 跨域资源共享)

请求类型:所有 http 请求类型

限制:需要浏览器和服务器同时支持

浏览器将 CORS 请求分成两类:

简单请求 
非简单请求 

简单请求

简单请求需满足以下两个条件:

1、请求类型为 head、get、post 
2、content-type 为表单或纯文本

简单请求浏览器会在请求头加入 Origin 来说明,本次请求来自哪个源(协议 + 域名 + 端口),若 Origin 指定的域名在服务器许可范围内,则会在响应头中携带以下 Access-Control 字段

Access-Control-Allow-Origin:type: string 必带。该值与请求头中的Origin字段相同或者为 * 
Access-Control-Allow-Credentials: type: boolean 可选。决定浏览器是否允许携带 cookie 发给服务器。若为 true 还需 XMR 对象中配置 XMLHttpRequest.prototype.withCredentials = 'true'
Access-Control-Expose-Headers: type: string 指定 XHR 对象所允许额外获取请求头字段。因为XMLHttpRequest对象的getResponseHeader()只能获取默认的字段。
(注意:若 Access-Control-Allow-Credentials 为 true,则 Access-Control-Allow-Origin 不能为 * )

非简单请求

会发送一次预检请求, 预检请求的请求头除了携带 Origin 字段还会携带 Access-Control-Request-Method 和 Access-Control-Request-Method 字段,分别用来说明请求方法和需要额外携带的请求头。若允许,服务器返回的响应头会携带以下 Access-Control 字段

Access-Control-Allow-Methods: type: string 必带。分别说明允许跨域的方法和是否允许跨域
Access-Control-Allow-Origin: type: string 必带。该值与请求头中的Origin字段相同或者为 * 
Access-Control-Max-Age: type: number 可选。指定本次预检请求的有效期,单位为秒
Access-Control-Allow-Credentials: type: boolean 可选。决定浏览器是否允许携带 cookie 发给服务器。若为 true 还需 XMR 对象中配置 XMLHttpRequest.prototype.withCredentials = 'true'
Access-Control-Expose-Headers: type: string 指定 XHR 对象所允许额外获取请求头字段。因为XMLHttpRequest对象的getResponseHeader()只能获取默认的字段。
(注意:若 Access-Control-Allow-Credentials 为 true,则 Access-Control-Allow-Origin 不能为 * )

6、nginx 反向代理接口跨域

Nginx 反向代理服务器

#proxy 服务器
server {
    listen       81;
    server_name  www.domain1.com;

    location / {
        proxy_pass   http://www.domain2.com:8080;  #反向代理,跨域目标接口
        proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
        index  index.html index.htm;

        # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
        add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时,可为*
        add_header Access-Control-Allow-Credentials true;
    }
}

7、nodejs 中间件

node + express + http-proxy-middleware 搭建一个 proxy 服务器

#proxy 服务器
var express = require('express');
var proxy = require('http-proxy-middleware');
var app = express();

app.use('/', proxy({
    // 代理跨域目标接口
    target: 'http://www.domain2.com:8080',
    changeOrigin: true,

    // 修改响应头信息,实现跨域并允许带cookie
    onProxyRes: function(proxyRes, req, res) {
        res.header('Access-Control-Allow-Origin', 'http://www.domain1.com');
        res.header('Access-Control-Allow-Credentials', 'true');
    },

    // 修改响应信息中的cookie域名
    cookieDomainRewrite: 'www.domain1.com'  // 可以为false,表示不修改
}));

app.listen(3000);
console.log('Proxy server is listen at port 3000...');

8、websocket

Socket.io 实现前端和 nodejs 双向通信

什么是单工、半双工、全工通信?

信息只能单向传送为单工;信息能双向传送但不能同时双向传送称为半双工;信息能够同时双向传送则称为全双工。