[转]同源策略和跨域请求解决方案
一、一个源的定义
如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源。
举个例子:
下表给出了相对http://a.xyz.com/dir/page.html同源检测的示例: URL 结果 原因 http://a.xyz.com/dir2/other.html 成功 协议,端口(如果有指定)和域名都相同 http://a.xyz.com/dir/inner/another.html 成功 协议,端口(如果有指定)和域名都相同 https://a.xyz.com/secure.html 失败 不同协议 ( https和http ) http://a.xyz.com:81/dir/etc.html 失败 不同端口 ( 81和80) http://a.opq.com/dir/other.html 失败 不同域名 ( xyz和opq)
二、同源策略是什么?
同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。所以xyz.com下的js脚本采用ajax读取abc.com里面的文件数据是会被拒绝的。
同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
三、基于jsonp实现的跨域请求
-
页面中的链接,重定向以及表单提交是不会受到同源策略限制的。
-
跨域资源的引入是可以的。但是js不能读写加载的内容。如嵌入到页面中的<script src="..."></script>,<img>,<link>,<iframe>等。
下面来分步举例详细阐述其中的奥妙:
1、先开两个项目,
项目1(http://127.0.0.1:8000/)
项目2(http://127.0.0.1:8100/)
项目1
url: url(r'index1/$',views.index1) views: def index1(request): return HttpResponse('wangjifei')
项目2
url: url(r'index2/$',views.index2) views : def index2(request): return render(request,'index2.html') index2.html: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script> $('#btn').click(function () { $.ajax({ url:"http://127.0.0.1:8000/index1/", type:'get', success:function (res) { console.log(res) } }) }) </script> </body> </html>
现在,打开使用浏览器打开 http://127.0.0.1:8100/index2/,点击页面上的 '提交' 按钮,会在console页面发现错误信息如下:
为什么报错呢?因为同源策略限制跨域发送ajax请求。
细心点的同学应该会发现我们的demo1项目其实已经接收到了请求并返回了响应,是浏览器对非同源请求返回的结果做了拦截。
再细心点的同学会发现,我们使用cdn方式引用的jQuery文件也是跨域的,它就可以使用。
同样是从其他的站点拿东西,script标签就可以。那我们能不能利用这一点搞点事情呢?
2、把index2.html中的代码改一下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script src="http://127.0.0.1:8000/index1/"></script> </body> </html>
现在刷新一下会出现如下错误:
看来后端返回的响应已经被拿到了,只不过把wangjifei当成了一个变量来使用,但是该页面上却没有定义一个名为wangjifei的变量。所以出错了。
3、那我们就在index2.html中定义一个wangjifei变量看看:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script> var wangjifei = 123 </script> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script src="http://127.0.0.1:8000/index1/"></script> </body> </html>
刷新发现不报错了,
4、我定义一个变量可以,那可不可以定义一个函数呢?
index2.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script> function wangjifei() { console.log('出手就要专业') } </script> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script src="http://127.0.0.1:8000/index1/"></script> </body> </html>
项目1中的views:也修改一下
def index1(request): return HttpResponse('wangjifei()')
刷新一下页面显示结果:
结果分析:返回的 wangjifei(),页面上拿到这个响应之后直接执行了wangjifei函数!
5、那函数中可不可以传递参数呢?我们试一下!
index2.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script> function wangjifei(res) { console.log(res) } </script> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script src="http://127.0.0.1:8000/index1/"></script> </body> </html>
项目1中的 views
from django.http import HttpResponse import json def index1(request): ret={'code':1,'msg':[110,119,120,12306]} res = json.dumps(ret) return HttpResponse(f'wangjifei({res})')
刷新之后显示结果:
果然传递参数也是可以的!我们通过script标签的跨域特性来绕过同源策略拿到想要的数据了!!!
这其实就是JSONP的简单实现模式,或者说是JSONP的原型:创建一个回调函数,然后在远程服务上调用这个函数并且将JSON 数据形式作为参数传递,完成回调。
将JSON数据填充进回调函数,这就是JSONP的JSON+Padding的含义。
但是我们更多时候是希望通过事件触发数据的获取,而不是像上面一样页面一刷新就执行了,这样很不灵活。
6、我们可以通过javascript动态的创建script标签来实现。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script> //自定义的函数 function wangjifei(res) { console.log(res) } //jquery给button绑定点击事件 $('#btn').click(function () { //创建一个script标签 var scriptEle = document.createElement('script'); //给标签添加src属性,并添加对应的属性值 http://127.0.0.1:8000/index1 $(scriptEle).attr('src','http://127.0.0.1:8000/index1'); //将创建好的标签添加到页面中,标签添加后就会自动触发get请求 $('body').append(scriptEle); //将标签移除 $(scriptEle).remove() }) </script> </body> </html>
这样当我们点击button按钮的时候,会在页面上插入一个script标签,然后从后端获取数据后再删除掉。
7、为了实现更加灵活的调用,我们可以把客户端定义的回调函数的函数名传给服务端,服务端则会返回以该回调函数名,将获取的json数据传入这个函数完成回调。这样就能实现动态的调用了。修改代码如下:
index2.html代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script> //自定义的函数 function xxx(res) { console.log(res) } //jquery给button绑定点击事件 $('#btn').click(function () { //创建一个script标签 var scriptEle = document.createElement('script'); //给标签添加src属性,并添加对应的属性值 http://127.0.0.1:8000/index1?callback=xxx $(scriptEle).attr('src','http://127.0.0.1:8000/index1?callback=xxx'); //将创建好的标签添加到页面中,标签添加后就会自动触发get请求 $('body').append(scriptEle); //将标签移除 $(scriptEle).remove() }) </script> </body> </html>
项目1中views:
from django.http import HttpResponse import json def index1(request): ret={'code':1,'msg':[110,119,120,12306]} res = json.dumps(ret) callback = request.GET.get('callback') return HttpResponse(f'{callback}({res})')
四、jQuery中getJSON方法介绍:
1、jQuery中有专门的方法实现jsonp。
index2.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script> //jquery给button绑定点击事件 $('#btn').click(function () { $.getJSON("http://127.0.0.1:8000/index1?callback=?",function (res) { console.log(res) }) }) </script> </body> </html>
要注意的是在url的后面必须要有一个callback参数,这样getJSON方法才会知道是用JSONP方式去访问服务,callback后面的那个?是jQuery内部自动生成的一个回调函数名。
2、但是如果我们想自己指定回调函数名,或者说服务上规定了回调函数名该怎么办呢?我们可以使用$.ajax方法来实现:
index2.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script> //jquery给button绑定点击事件 $('#btn').click(function () { $.ajax({ //要访问的url url:"http://127.0.0.1:8000/index1/", //要处理的数据类型jsonp dataType:'jsonp', //自定义回调函数名必要参数 jsonp:'callback', //自定义回调函数名,url中callback=后面的函数名 jsonpcallback:'wangjifei' }) }); //回调函数 function wangjifei(res) { console.log(res) } </script> </body> </html>
views:
from django.http import HttpResponse import json def index1(request): ret={'code':1,'msg':[110,119,120,12306]} res = json.dumps(ret) callback = request.GET.get('callback') return HttpResponse(f'wangjifei({res})')
3、用ajax技术通常将回调函数写在成功回调函数的位置:
index2.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script> //jquery给button绑定点击事件 $('#btn').click(function () { $.ajax({ //要访问的url url:"http://127.0.0.1:8000/index1/", //要处理的数据类型jsonp dataType:'jsonp', //success回调 success:function (res) { console.log(res) } }) }); //回调函数 function wangjifei(res) { console.log(res) } </script> </body> </html>
最后来一个jsonp的实际应用:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="show-tv">提交</button> <div class="tv-list"></div> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script> $("#show-tv").click(function () { $.ajax({ url: "http://www.jxntv.cn/data/jmd-jxtv2.html? callback=list&_=1454376870403", dataType: 'jsonp', jsonp: 'callback', jsonpCallback: 'list', success: function (data) { var weekList = data.data; console.log(weekList); var $tvListEle = $(".tv-list"); $.each(weekList, function (k, v) { var s1 = "<p>" + v.week + "列表</p>"; $tvListEle.append(s1); $.each(v.list, function (k2, v2) { var s2 = "<p><a href='" + v2.link + "'>" + v2.name + "</a></p>"; $tvListEle.append(s2) }); $tvListEle.append("<hr>"); }) } }) }); </script> </body> </html>
五、基于Core方法解决跨域请求
-
我们介绍了jsonp解决跨域请求问题,这种解决方式很好的诠释了跨域请求的本质,但是略显麻烦,是否还记得在我们不做任何处理的时候,跨域请求时候浏览器给我们报的错误不?翻译过来就是因为响应头没有指定Access-Control-Allow-Origin所允许原始的请求路径,因此原始请求路径http://127.0.0.1:8001不被允许访问。 基于上述的原因解释,我们只需要在响应的内容加入上述这样的授权字段,便可解决。
-
简单请求的定义:
只要同时满足以下两大条件,就属于简单请求,不满足就是复杂请求!!!
1.(1) 请求方法是以下三种方法之一:-- HEAD,GET,POST
2.(2)HTTP的头信息不超出以下几种字段:-- Accept
-- Accept-Language
-- Content-Language
-- Last-Event-ID
-- Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
由于django的所有请求响应都要走中间件,所以可以写一个跨域的中间件来解决跨域问题
from django.utils.deprecation import MiddlewareMixin class MyCore(MiddlewareMixin): def process_response(self, request, response): response['Access-Control-Allow-Origin'] = "*" //简单请求 if request.method == "OPTIONS": # 复杂请求 预检 response['Access-Control-Allow-Headers'] = "Content-Type" response['Access-Control-Allow-Methods'] = "POST, DELETE, PUT" return response
原文地址:https://www.cnblogs.com/sanxiao/p/11604442.html
- 【Spring实战】—— 4 Spring中bean的init和destroy方法讲解
- 基于AngularJS的过滤与排序
- 【Spring实战】—— 5 设值注入
- 科学家预测:未来100万年人类将变成半机械人类
- 【Spring实战】—— 8 自动装配
- 【Spring实战】—— 7 复杂集合类型的注入
- 【Spring实战】—— 6 内部Bean
- 几款可替代Dreamweaver的HTML5开发工具
- Linux下的Telnet设置方法介绍
- 2017年11月互联网和相关服务业保持快速增长
- 深度学习胸部x射线
- C+实现神经网络之壹—Net类的设计和神经网络的初始化
- 死亡不可避免,但何时死,人工智能或有发言权
- 打开手机的这个功能,微信支付宝不怕盗刷!
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- ajax无刷新页面切换,历史记录后退前进解决方案
- 一起来学演化计算-SBX模拟二进制交叉算子和DE差分进化算子
- 通过与C++程序对比,彻底搞清楚JAVA的对象拷贝
- Spring - application.yml 数字读取错误、eg: 000001
- Swagger2 UI 提示"请确保swagger资源接口正确"解决办法
- Manytasking Jmetal 代码反向解析 2_MMDTLZ
- GitLab服务器搭建
- MATP1生成测试SolutionSet
- 以猜数字游戏引出的分治算法的理解与思考
- MATP ManyTask Multitask Problem 和 Solution 的变量范围
- Sinopia安装部署
- Nginx的405 not allowed错误解决
- Linux免密登陆
- 一起来学matlab-matlab学习笔记8 基本绘图命令_2基本绘图操作
- 用一个图书库实例搞懂二分搜索树的底层原理