博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用正确的姿势跨域
阅读量:4326 次
发布时间:2019-06-06

本文共 6742 字,大约阅读时间需要 22 分钟。

请求跨域问题的产生原因是浏览器的同源策略(Same origin policy),这是有Netscape提出的一个著名的安全策略,对于浏览器而言,它提供了最基本也是最核心的安全功能。它所指的同源是域名、协议、端口都相同。

从wiki百科上截了个例子说明:

从图中可以看出,三者只要任何一个不相同,都会导致Failure.

什么样的姿势跨域,才能成功的跨域?

贴上一份简单的node代码,用以说明服务端的情况:

var http = require('http');var fs = require('fs');var reg = /(^|\?|&)callback=\w*/;var MINE_TYPE = {  'css': 'text/css',  'html': 'text/html',  'js': 'text/javascript',  'txt':'text/plain'};http.createServer(function(req,res){  var _url = req.url;  var data = _url.indexOf('callback') >=0 ? req.url.match(reg)[0].substr(10)+'("XMLHttpRequest is success")':'XMLHttpRequest is success';  res.writeHead(200,{'Content-Type':MINE_TYPE['txt'],'Access-Control-Allow-Origin':'http://192.168.1.162:9988'});  res.end(data);}).listen(3003,'192.168.1.162');function _server(port){  http.createServer(function(req,res){    var pathname = req.url.substr(1);    var _ext = pathname.split('.').pop();    fs.readFile(pathname,'utf-8',function(err,data){      res.writeHead(200,{'Content-Type':MINE_TYPE[_ext]});      res.end(data)    })  }).listen(port,'192.168.1.162');    }_server(8899);_server(9988);

先以普通的姿势来跨一次(本文调试使用端口条件处理同源):

html:

发送请求

js:

$('.xmlhttprequset').click(function(){    $.get('http://192.168.1.162:3003',function(res){        console.log(res);    })})

我们在浏览器url框输入192.168.1.162:8899/test.html和192.168.1.162:9988/test.html打开两个页面,分别点击页面上的"发送请求",得到以下结果:

XMLHttpRequest cannot load http://192.168.1.162:3003/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://192.168.1.162:port' is therefore not allowed access.

看到这个错误,心里一如既往的爽...那么爽完之后,该解决错误了,最常见的是jsonp处理跨域问题:

js:

$('.xmlhttprequset').click(function(){  $.ajax({    type:'get',    dataType:'jsonp',    url:'http://192.168.1.162:3003',    success:function(res){      console.log(res);    }  });})

于是,我们就能够在控制台看到打印出来的东西了...

那么我不想使用jsonp呢,能否实现跨域呢?答案是肯定的。而且还可以允许9988端口跨域请求,对8899端口进行"丑拒",就是这么任性~ 办法就是在response头配置"Access-Control-Allow-Origin"参数的值来控制给不给跨,不给跨,给跨,跨... ←_← 这真的只是回声.

于是我们将server.js代码改了改:

http.createServer(function(req,res){  var _url = req.url;  var data = _url.indexOf('callback') >=0 ? req.url.match(reg)[0].substr(10)+'("success")':'success';  res.writeHead(200,{'Content-Type':MINE_TYPE['txt'],'Access-Control-Allow-Origin':'http://192.168.1.162:9988'});  res.end(data);}).listen(3003,'192.168.1.162');

然后将jsonp请求的代码换回get请求的代码,重启这个js,切换到浏览器,刷新2个页面,再次点击请求,效果如下:

:9988

success

:8899

XMLHttpRequest cannot load http://192.168.1.162:3003/. The 'Access-Control-Allow-Origin' header has a value 'http://192.168.1.162:9988' that is not equal to the supplied origin. Origin 'http://192.168.1.162:8899' is therefore not allowed access.// 强行暴击

那么现在姿势明确了,两个姿势:正常请求+response设置允许域,jsonp。

jsonp 实现原理

jsonp通过添加一个<script>标签,将该标签的src指向请求资源的接口,并且需要在请求中带上一个callback参数,script的src是不受浏览器的同源策略限制的,所以只要后端将数据包装在这个callback的方法里返回即可,于是我们有了这段代码:

server.js:

var data = _url.indexOf('callback') >=0 ? req.url.match(reg)[0].substr(10)+'("success")':'success';// 如果包含callback参数,则将参数以callback的值为方法名包装成一个执行函数,否则直接返回数据

jsonp在jQuery中的实现为:

var oldCallbacks = [],    rjsonp = /(=)\?(?=&|$)|\?\?/;// 默认jsonp设置jQuery.ajaxSetup({    jsonp: "callback",    jsonpCallback: function () {        var callback = oldCallbacks.pop() || (jQuery.expando + "_" + (nonce++));        this[callback] = true;        return callback;    }});jQuery.ajaxPrefilter("json jsonp", function (s, originalSettings, jqXHR) {    var callbackName, overwritten, responseContainer,        jsonProp = s.jsonp !== false && (rjsonp.test(s.url) ?            "url" :            typeof s.data === "string" &&            (s.contentType || "")                .indexOf("application/x-www-form-urlencoded") === 0 &&            rjsonp.test(s.data) && "data"        );    // jsonp 判断    if (jsonProp || s.dataTypes[0] === "jsonp") {        // 生成 callback 名称        callbackName = s.jsonpCallback = jQuery.isFunction(s.jsonpCallback) ?            s.jsonpCallback() :            s.jsonpCallback;        // url处理 加入 callback        if (jsonProp) {            s[jsonProp] = s[jsonProp].replace(rjsonp, "$1" + callbackName);        } else if (s.jsonp !== false) {            s.url += (rquery.test(s.url) ? "&" : "?") + s.jsonp + "=" + callbackName;        }        // 方法执行后取数据        s.converters["script json"] = function () {            if (!responseContainer) {                jQuery.error(callbackName + " was not called");            }            return responseContainer[0];        };        // Force json dataType        s.dataTypes[0] = "json";        // 添加callback这个方法        overwritten = window[callbackName];        window[callbackName] = function () {            responseContainer = arguments;        };        // 毁尸灭迹处理        jqXHR.always(function () {            // 如果之前不存在这个方法,删除            if (overwritten === undefined) {                jQuery(window).removeProp(callbackName);                // 如果之前就存在这个方法,恢复            } else {                window[callbackName] = overwritten;            }            //             if (s[callbackName]) {                // 确保安全处理,不影响其他项                s.jsonpCallback = originalSettings.jsonpCallback;                // 预留着                oldCallbacks.push(callbackName);            }            // 如果是个函数,携带数据调用            if (responseContainer && jQuery.isFunction(overwritten)) {                overwritten(responseContainer[0]);            }            responseContainer = overwritten = undefined;        });        // 归为script        return "script";    }});// jsonp实现,生成script及之后移除jQuery.ajaxTransport("script", function (s) {    // 这个函数指明了只处理跨域请求    if (s.crossDomain) {        var script, callback;        return {            send: function (_, complete) {                script = jQuery("

简单过一遍,就是这样了...然后扔出整理后的,不加各种判断的,简单的,只有20行代码的实现,帮助理解上面这么长长的一堆代码:

js:

function getInfo(url, callback, _callback){    url += url.indexOf('?')>=0 ? '&callback=' + callback : '?callback='+callback;  // url处理    var overWriteContent;    var script = document.createElement('script');    script.src = url;    overWritten = window.dataBack;    window[callback] = function(){      overWriteContent = arguments[0];    };                                                          //  生成callback方法,挂在window下    document.head.appendChild(script);                          //  添加script标签    script.onload = function(e){      document.head.removeChild(script);                        //  删除script标签      if(overWritten === undefined) delete window[callback];    //  销毁window下的callback方法      if(e.type === 'load'){        _callback(overWriteContent);                            //  带上数据执行回调      } else{        console.error('error:failed to load the resource');      }    }}

如有不正之处,感谢指正,同时欢迎小伙伴们交流讨论~

水平较渣,不喜勿喷,谢谢!

转载于:https://www.cnblogs.com/ys-ys/p/5757905.html

你可能感兴趣的文章
HDU 4162 Shape Number
查看>>
HDU 5211 Mutiple 水题
查看>>
linux中ctrl+z、ctrl+d和ctrl+c的区别
查看>>
mysql数据库的简单操作
查看>>
走过2013,走进2014
查看>>
修改tomcatlog输出等级
查看>>
数据结构 堆栈
查看>>
微信公众号开发C#系列-6、消息管理-普通消息接受处理
查看>>
RDIFramework.NET V2.9版本 WinFom部分新增与修正的功能
查看>>
AOP实战
查看>>
最大堆,最小堆及堆排序
查看>>
学习日记
查看>>
EasyNVR RTSP转RTMP-HLS流媒体服务器前端构建之:内部搜索功能的实现
查看>>
四则运算--封装5.1
查看>>
Python中dunder名称的来历
查看>>
不知道下一步该怎么走
查看>>
并查集,合根植物
查看>>
C++中String类的字符串分割实现
查看>>
HelloWorld
查看>>
CSS3-animations
查看>>