跨域问题的产生于处理

什么是跨域

1、先了解一下域名的组成:

       

JS 出于安全方面的考虑,不允许跨域调用其他页面的对象,那什么是跨域呢,简单地理解就是因为浏览器同源策略的限制,a.com 域名下无法操作 b.com 或是 c.a.com 域名下的对象

 

#

URL

说明

是否允许通信

1

http://www.a.com/a.js
http://www.a.com/b.js

同一域名下

允许

2

http://www.a.com/lab/a.js
http://www.a.com/script/b.js

同一域名下不同资源地址

允许

3

http://www.a.com:8080/a.js
http://www.a.com/b.js

同一域名,不同端口

不允许

4

http://www.a.com/a.js
https://www.a.com/b.js

同一域名,不同协议

不允许

5

http://www.a.com/a.js
http://20.33.23.61/b.js

域名与域名对应的IP

不允许

6

http://www.a.com/a.js
http://script.a.com/b.js

主域名相同,子域名不同

不允许

7

http://www.a.com/a.js
http://a.com/b.js

主域名相同,子域名不同

不允许

8

http://www.itcast.cn/a.js
http://www.a.com/b.js

不同域名

不允许

 

2、广义的跨域

跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的,其实我们通常所说的跨域是狭义的,是由浏览器同源策略限制的一类请求场景

知识点:同源策略

同源策略限制一个源的资源(文档或脚本)如何与另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。它的存在可以保护用户隐私信息、防止身份伪造等。

同源策略限制内容有:

  1. Cookie、LocalStorage、IndexedDB 等存储性内容
  2. DOM 节点
  3. AJAX 请求不能发送

跨域简单举例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>a页面</title>
</head>

<body>
    <button onclick="clickButton()">A点击</button>
</body>
<script src="./js/jquery-1.10.2.min.js"></script>
<script>
    function clickButton() {
        $.ajax({
            url:'http://www.wfm.com:3000/server',
            success:function(data){
                console.log(data);
            },
            error:function(e){
                console.log(e);
            }
        });

</script>

</html>

    

处理跨域的方法

  • JSONP

(1)原生

html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>a页面</title>
</head>

<body>
    <button onclick="clickButton()">A点击</button>
</body>
<script src="./js/jquery-1.10.2.min.js"></script>
<script>
    function clickButton() {

        // jsonp(只能用于get请求)
        // 方式一
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = 'http://www.wfm.com:3000/server?callback=handleCallback';
        document.head.appendChild(script);


        // 回调执行函数
        function handleCallback(res) {
            alert("回调函数执行!!")
            alert(JSON.stringify(res));
        }

    }
</script>

</html>

  nodejs:

var express = require('express');
var router = express.Router();
var http = require('http');
var querystring = require('querystring');
var url = require('url');

/* GET home page. */
router.get('/server', function (req, res, next) {
  var urlPath = url.parse(req.url).pathname;
  var param = querystring.parse(req.url.split('?')[1]);

  res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

  var data = { msg: 'helloworld' };
  data = JSON.stringify(data);

  var callback = param.callback + '(' + data + ');';
  res.write(callback);

  res.end();
});

module.exports = router;

 结果:

    

(2)Jquery ajax

html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>a页面</title>
</head>

<body>
    <button onclick="clickButton()">A点击</button>
</body>
<script src="./js/jquery-1.10.2.min.js"></script>
<script>
    function clickButton() {

        //方式二
        $.ajax({
            url: 'http://www.wfm.com:3000/server?callback=handleCallback',
            jsonp: 'callback',//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)
            type: 'get',
            dataType: 'jsonp',//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以不写,jQuery会把数据拿出来放到success函数的参数里
            jsonpCallback: 'handleCallback',
            success(data) {
                alert('123')
                console.log(data);
            },
            error(XMLHttpRequest, textStatus, errorThrown) {
                console.error(XMLHttpRequest, textStatus, errorThrown)
            }
        });


        // 回调执行函数
        function handleCallback(res) {
            alert("回调函数执行!!")
            alert(JSON.stringify(res));
        }

    }
</script>

</html>

nodejs

var express = require('express');
var router = express.Router();
var http = require('http');
var querystring = require('querystring');
var url = require('url');

/* GET home page. */
router.get('/server', function (req, res, next) {
  var urlPath = url.parse(req.url).pathname;
  var param = querystring.parse(req.url.split('?')[1]);

  res.writeHead(200, { 'Content-Type': 'application/json;charset=utf-8' });

  var data = { msg: 'helloworld' };
  data = JSON.stringify(data);

  var callback = param.callback + '(' + data + ');';
  res.write(callback);

  res.end();
});

module.exports = router;

Tip:为什么呢????

原因就是浏览器是允许三个标签跨域加载资源的:

  <img src="path/to/xxx">

  <link href="path/to/xxx">

  <script src="path/to/xxx"></script>

JSONP利用 <script> 标签的这个开放策略,网页可以得到从其他来源动态产生的 JSON 数据。

但是这种方式只是用与get请求,不推荐使用

  • CORS 跨域资源共享

(1)介绍

跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源

(2)带有身份凭证的CORS

  • 默认情况下的跨域请求都是不会把cookie发送给服务器的,在需要发送的情况下,如果是xhr,那么需要设置xhr.withCredentials=true,
  • 如果是采用fetch获取的话,那么需要在request里面设置 credentials:'include',
  • 但是如果服务器在预请求的时候没返回Access-Control-Allow-Crenditials:true的话,那么在实际请求的时候,cookie是不会被发送给服务器端的,要特别注意对于简单的get请求,不会有预请求的过程,
  • 那么在实际请求的时候,如果服务器没有返回Access-Control-Allow-Crenditials:true的话那么响应结果浏览器也不会交给请求者

(3)HTTP响应首部字段

  • Access-Control-Allow-Origin: <origin> | *
  • Access-Control-Expose-Headers 头让服务器把允许浏览器访问的头放入白名单
  • Access-Control-Max-Age 头指定了preflight请求的结果能够被缓存多久
  • Access-Control-Allow-Credentials
    头指定了当浏览器的credentials设置为true时是否允许浏览器读取response的内容。
  • Access-Control-Allow-Methods 首部字段用于预检请求的响应。其指明了实际请求所允许使用的 HTTP 方法。
  • Access-Control-Allow-Headers 首部字段用于预检请求的响应。其指明了实际请求中允许携带的首部字段

 

后台服务CORS解决跨域(nodejs)

var express = require('express');
var router = express.Router();
var url=require('url');
var app=express();
var allowCrossDomain = function(req, res, next) {
  res.header('Access-Control-Allow-Origin', 'http://127.0.0.1:8899');
  res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
  res.header('Access-Control-Allow-Headers', 'Content-Type');
  res.header('Access-Control-Allow-Credentials','true');
  next();
};
app.use(allowCrossDomain);

/* GET users listing. */
app.post('/', function(req, res, next) {
  res.send('respond with a resource');
});

module.exports = app;
  • Nginx代理

通过nginx配置一个代理服务器(域名与localhost相同,端口不同)做跳板机,反向代理访问wfm接口,实现跨域

server{
    # 监听8899端口
    listen 8899;
    # 域名是localhost
    server_name localhost;
    #凡是localhost:8899/api这个样子的,都转发到真正的服务端地址http://www.wfm.com:3000
    location ^~ /users {
        proxy_pass http://www.wfm.com:3000;
    }    
}

Tip:配置之后就不需要前端做什么修改了,一般我们在前后端分离项目中开发阶段会采用这种方式,但不是所有场景都能这样做,例如后端接口是一个公共的API,比如一些公共服务获取用户信息等

(1)iframe_a.html:(http://www.wfm1.com:7799/a.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>iframe_a页面</title>
</head>
<body>
    
</body>
<script>
    var proxy = function(url, callback) {
    var state = 0;
    var iframe = document.createElement('iframe');

    // 加载跨域页面
    iframe.src = url;

    // onload事件会触发2次,第1次加载跨域页,并留存数据于window.name
    iframe.onload = function() {
        if (state === 1) {
            // 第2次onload(同域proxy页)成功后,读取同域window.name中数据
            callback(iframe.contentWindow.name);
            destoryFrame();

        } else if (state === 0) {
            // 第1次onload(跨域页)成功后,切换到同域代理页面
            iframe.contentWindow.location = 'http://www.wfm1.com:7799/iframe_proxy.html';
            state = 1;
        }
    };

    document.body.appendChild(iframe);

    // 获取数据以后销毁这个iframe,释放内存;这也保证了安全(不被其他域frame js访问)
    function destoryFrame() {
        iframe.contentWindow.document.write('');
        iframe.contentWindow.close();
        document.body.removeChild(iframe);
    }
};

// 请求跨域b页面数据
proxy('http://www.wfm2.com:6699/iframe_b.html', function(data){
    alert(data);
});

</script>
</html>

(2)iframe_proxy.html:(http://www.wfm1.com/iframe_proxy.html)
中间代理页,与iframe_a.html同域,内容为空即可。

(3)iframe_b.html:(http:// www.wfm2.com /iframe_b.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>iframe_b页面</title>
</head>
<body>
    iframe_b页面
</body>
<script>
    window.name = 'This is domain2 data!';
</script>
</html>

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值