阅读 135

JS中常用的8种跨域方式讲解

1. 什么是跨域

简单的来说就是:

在A域名下向B域名发起了 request。

一个完整的URL应该包含 http(协议)://www.baidu.com(域名):80(端口)/index.html(路由)。

同源策略 就是指的 协议,域名,端口必须保持一致。任何不符合同源策略的请求 request 都属于跨域了。

2.通过jsonp跨域

index.html页面中

  <!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
  </head>
  <body>
    <script>
        function jsonp({url, params, cb}){
            let script = document.createElement('script');
            window[cb] = function( data ){
                resolve(data);
                document.body.removeChild(script);
            }
            params = {...params, cb} //wd=b&cb=show
            let arrs = [];
            for(let key in params){
                arrs.push(`${key}=${params[key]}`);
            }
            script.src = `${url}?${arrs.join('&')}`;
            document.body.appendChild(script);
        }
        
    //只能发送get 请求,不支持 post put delete
    //不安全 xss攻击  不采用
    jsonp({
        url: 'http://localhost:3000/say',
        params: {wd: 'hello'},
        cb: 'show'  //回调函数
    }).then(data => {
        console.log(data);
    });
    </script>
</body>
复制代码

server.js

let express = require('express');
let app = express();
app.get('/say', function(req, res){
    let {wd, cb} = req.query;
    res.end(`${cb}('world')`);  //返回 'show('world')'
});
复制代码

3. 通过CORS跨域

cors.html

  <!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
  </head>
  <body>
    hello cors.html
    <script>
        let xhr = new XMLHttpRequest;
        document.cookie = 'nam=zfpx';
        xhr.withCredentials = true;
        xhr.open('PUT', 'http://localhost:4000/getData', true);
        xhr.setRequestHeader('name','zfpx');
        xhr.onreadystatechange = function(){
            if(xhr.readyState === 4){
                if(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304){
                    console.log(xhr.response);
                    console.log(xhr.getResponseHeader('name'));
                }
            }
        }
        xhr.send();
    </script>
  </body>    
复制代码
  • cors.html.server.js
let express = require('express');
let app = express();
app.use(express.static(__dirname));
app.listen(3000);
复制代码

  • cors.target.js
let express = require('express');
let app = express();
let whitList = ['http://localhost:3000'];  //用来访问 cors.html的页面
app.use(function(req, res, next){
    let origin = req.headers.origin;
    if(whitList.includes(origin)){
        //设置允许哪个源访问我
        res.setHeader('Access-Control-Allow-Origin', origin);
        //允许携带哪个请求头访问我
        res.setHeader('Access-Control-Allow-Headers', 'name');
        //允许哪个方法访问我
        res.setHeader('Access-Control-Allow-Methods', 'PUT');
        //允许携带cookie, 与前端 xhr.withCredentials = true; 相呼应
        res.setHeader('Access-Control-Allow-Credentials', true);
        //允许的有效期: 第二次发送options方法的时间间隔
        res.setHeader('Access-Control-Max-Age', 60); //60s
        //允许设置的返回头 与前端 console.log(xhr.getResponseHeader('name'));相呼应
        res.setHeader('Access-Control-Expose-Headers', 'name');
        if(req.method === 'OPTIONS'){
            res.end();  //浏览器在发起put请求前会发送一个options方法,来验证允许访问的各种参数
        }
    }
});

app.put('/getData', function(req, res){
    console.log(req.headers);
    res.setHeader('name','jw');
    res.end('hello');
});

app.use(express.static(__dirname));
app.listen(4000)
复制代码

从 http://localhost:3000 向 http://localhsot:4000 发送put 请求。

4. 通过postMessage跨域

通过 localhost:3000 访问 a.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <iframe src="http://localhost:4000/b.html" frameborder="0" id="frame" onload="load()"></iframe>
  <script>
    function load() {
        let iframe = document.getElementById('fram');
        frame.contentWindow.postMessage('hello', 'http://localhost:4000');  
        window.onmessage = function(e){
            console.log(e.data); //打印 world
        }
    }
    </script>
 </body>
复制代码

通过localhost:4000返回 b.html

....
<script>
    window.onmessage = function(e){
        console.log(e.data); // 打印 hello
        e.source.postMessage('world', e.origin);
    }
</script>
....
复制代码

5. 通过window.name 跨域

默认情况下 window.name 为 "" 有a.html, b.html, c.html. 其中a.html 和 b.html 通过http://localhost:3000访问,c.html通过 http://localhost:4000访问.

a.html

....
  a和b是同域的 http://localhost:3000
  c是独立的  http://localhost:4000
  a获取c的数据
  a先引用c c把值放到window.name, 把a引用的地址改到b
  <iframe src="http://localhost:4000/c.html" frameborder="0" onload="load()" id="iframe"></iframe>
  <script>
    let first = true
    function load() {
      if(first){
        let iframe = document.getElementById('iframe');
        iframe.src = 'http://localhost:3000/b.html';
        first = false;
      }else{
        console.log(iframe.contentWindow.name);
      }
    }
....

复制代码

c.html

...
  <script>
    window.name = 'hello a.html'  
  </script>
...
复制代码

b.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  
</body>
</html>
复制代码

a.html 中的控制台中会打印出 hello a.html

6.通过hash方式跨域

a.html

...
  <!-- 路径后面的hash值可以用来通信  -->
  <!-- 目的a想访问c -->
  <!-- a给c传一个hash值 c收到hash值后  c把hash值传递给b b将结果放到a的hash值中-->
  <iframe src="http://localhost:4000/c.html#from_a"></iframe>
  <script>
    window.onhashchange = function () {
      console.log(location.hash);  //打印from_c
    }
  </script>
...
复制代码

b.html

...
 <script>
    window.parent.parent.location.hash = location.hash  
  </script>
...
复制代码

c.html

...
<script>
  console.log(location.hash);  //打印from_a
  let iframe = document.createElement('iframe');
  iframe.src = 'http://localhost:3000/b.html#form_c';
  document.body.appendChild(iframe);
</script>
...

复制代码

7.通过domain方式跨域

a.html

...
 <!-- 域名 一级域名二级域名 -->
  <!-- www.baidu.com -->
  <!-- viode.baidu.com -->
  <!-- a是通过 http://a.zf1.cn:3000/a.html -->
  helloa
  <iframe src="http://b.zf.cn:3000/b.html" frameborder="0" onload="load()" id="frame"></iframe>
  <script>
    document.domain = 'zf.cn'
    function load() {
      console.log(frame.contentWindow.a);
    }
  </script>
...
复制代码

b.html

...
  hellob
   <script>
      document.domain = 'zf.cn'
     var a = 100;
   </script>
...
复制代码

8.通过websocket方式跨域

socket.html

...
let socket = new WebSocket('ws://localhost:3000');
socket.onopen = function(){
    socket.send('hello');
}
socket.onmessage = function(e){
    console.log(e.data); //打印world
}
...
复制代码

server.js

let express = require('express');
let app = express();
let WebSocket = require('ws');
let wss = new WebSocket.Server({port: 3000});
wss.on('connection', function(ws){
    console.log(data); // 打印 hello
    ws.send('world');
});
复制代码

9.通过nginx方式跨域

a.html

...
 <script>
    let xhr = new XMLHttpRequest;
    xhr.open('get','http://localhost/a.json',true);
    xhr.onreadystatechange = function () {
      if(xhr.readyState === 4){
        if(xhr.status>=200 && xhr.status < 300 || xhr.status ===304){
          console.log(xhr.response);
          console.log(xhr.getResponseHeader('name'));
        }
      }
    }
    xhr.send();
  </script>
...
复制代码

nginx.conf

location / {
    // 没有配置OPTIONS的话,浏览器如果是自动识别协议(http or https),那么浏览器的自动OPTIONS请求会返回不能跨域
    if ($request_method = OPTIONS ) {
        add_header Access-Control-Allow-Origin "$http_origin";
        add_header Access-Control-Allow-Methods "POST, GET, PUT, OPTIONS, DELETE";
        add_header Access-Control-Max-Age "3600";
        add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization";
        add_header Access-Control-Allow-Credentials "true";
        add_header Content-Length 0;
        add_header Content-Type text/plain;
        return 200;
    }
    add_header 'Access-Control-Allow-Origin' '$http_origin';
    add_header 'Access-Control-Allow-Credentials' 'true';
    add_header 'Access-Control-Allow-Methods' 'GET, PUT, POST, DELETE, OPTIONS';
    add_header 'Access-Control-Allow-Headers' 'Content-Type,*';
    proxy_pass http://127.0.0.1:8080;
    }
}
复制代码

nginx的方式有些类似cors。

10.小结

平时工作中可能熟知cors 和 jsonp, 这里大概将其原理实现了。以后再遇到面试官问你。就可以淡定的告诉他。然后还可以介绍其他的几种方式。用跨域的方式震慑住他。哈哈哈~~

文章分类
前端