django框架进阶-解决跨域问题
#######################################
"""
一、为什么会有跨域问题? 是因为浏览器的同源策略是对ajax请求进行阻拦了,但是不是所有的请求都给做跨域,像是一般的href属性,a标签什么的都不拦截。
同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,
如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。 一个源的定义
如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源。 举个例子:
下表给出了相对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)
"""
#######################################
举例说明
demo1 8002端口
# urls.py
urlpatterns = [
url(r'^abc/', views.abc),
] # views.py
def abc(request):
return HttpResponse("rion")
demo2 8000端口
# urls.py
urlpatterns = [
url(r'^xyz/', views.xyz),
]
# iews.py
def xyz(request):
return render(request, "xyz.html")
# xyz.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>xyz</title>
</head>
<body>
<button id="b1">点我</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
$("#b1").click(function () {
$.ajax({
url: "http://127.0.0.1:8002/abc/",
type: "get",
success:function (res) {
console.log(res);
}
})
});
</script>
</body>
</html>
现在,打开使用浏览器打开http://127.0.0.1:8000/xyz/,点击页面上的 '点我' 按钮,会在console页面发现错误信息如下:
为什么报错呢?因为同源策略限制跨域发送ajax请求。
#######################################
""" 二、解决跨域问题的两种方式
JSONP
CORS """
#######################################
细心点的同学应该会发现我们的demo1项目其实已经接收到了请求并返回了响应,是浏览器对非同源请求返回的结果做了拦截。
再细心点的同学会发现,我们使用cdn方式引用的jQuery文件也是跨域的,它就可以使用。
同样是从其他的站点拿东西,script标签就可以。那我们能不能利用这一点搞点事情呢?
把xyz.html中的代码改一下:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>xyz</title>
</head>
<body>
<button id="b1">点我</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script src="http://127.0.0.1:8002/abc/"></script>
</body>
</html>
会报错,rion is not define ,该页面上却没有定义一个名为rion的变量。所以出错了。
那我定义一个rion变量还不行吗?
把xyz.html中的代码改一下:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>xyz</title>
</head>
<body>
<button id="b1">点我</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
var rion = 100;
</script>
<script src="http://127.0.0.1:8002/abc/"></script>
</body>
</html>
这次就不会报错了。
我定义一个变量可以,那可不可以定义一个函数呢?
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>xyz</title>
</head>
<body>
<button id="b1">点我</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
function rion() {
console.log("选我不后悔!");
}
</script>
<script src="http://127.0.0.1:8002/abc/"></script>
</body>
</html>
同时把返回的响应也改一下:
def abc(request):
return HttpResponse("rion()")
我返回的 rion(),页面上拿到这个响应之后直接执行了rion函数!
那函数中可不可以传递参数呢?我们试一下!
把xyz.html中的代码改一下:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>xyz</title>
</head>
<body>
<button id="b1">点我</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
function rion(res) {
console.log(res);
}
</script>
<script src="http://127.0.0.1:8002/abc/"></script>
</body>
</html>
demo1中的视图函数:
def abc(request):
res = {"code": 0, "data": ["SNIS-561", "SNIS-517", "SNIS-539"]}
return HttpResponse("rion({})".format(json.dumps(res)))
果然传递参数也是可以的!
我们通过script标签的跨域特性来绕过同源策略拿到想要的数据了!!!
这其实就是JSONP的简单实现模式,或者说是JSONP的原型:创建一个回调函数,然后在远程服务上调用这个函数并且将JSON 数据形式作为参数传递,完成回调。
将JSON数据填充进回调函数,这就是JSONP的JSON+Padding的含义。
但是我们更多时候是希望通过事件触发数据的获取,而不是像上面一样页面一刷新就执行了,这样很不灵活。
其实这很好解决,我们可以通过javascript动态的创建script标签来实现。
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>xyz</title>
</head>
<body>
<button id="b1">点我</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
function rion(res) {
console.log(res);
}
function addScriptTag(src){
var scriptEle = document.createElement("script");
$(scriptEle).attr("src", src);
$("body").append(scriptEle);
$(scriptEle).remove();
}
$("#b1").click(function () {
addScriptTag("http://127.0.0.1:8002/abc/")
})
</script>
</body>
</html>
这样当我们点击b1按钮的时候,会在页面上插入一个script标签,然后从后端获取数据。
为了实现更加灵活的调用,我们可以把客户端定义的回调函数的函数名传给服务端,服务端则会返回以该回调函数名,将获取的json数据传入这个函数完成回调。
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>xyz</title>
</head>
<body>
<button id="b1">点我</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
function rion(res) {
console.log(res);
} function addScriptTag(src) {
var scriptEle = document.createElement("script");
$(scriptEle).attr("src", src);
$("body").append(scriptEle);
$(scriptEle).remove();
}
$("#b1").click(function () {
addScriptTag("http://127.0.0.1:8002/abc/?callback=rion")
});
</script>
</body>
</html>
view.py
def abc(request):
res = {"code": 0, "data": ["SNIS-561", "SNIS-517", "SNIS-539"]}
func = request.GET.get("callback")
return HttpResponse("{}({})".format(func, json.dumps(res)))
这样就能实现动态的调用了。
jQuery中有专门的方法实现jsonp。
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>xyz</title>
</head>
<body>
<button id="b1">点我</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
$("#b1").click(function () {
$.getJSON("http://127.0.0.1:8002/abc/?callback=?", function (res) {
console.log(res);
})
});
</script>
</body>
</html>
要注意的是在url的后面必须要有一个callback参数,这样getJSON方法才会知道是用JSONP方式去访问服务,callback后面的那个?是jQuery内部自动生成的一个回调函数名。
但是如果我们想自己指定回调函数名,或者说服务上规定了回调函数名该怎么办呢?我们可以使用$.ajax方法来实现:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>xyz</title>
</head>
<body>
<button id="b1">点我</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
$("#b1").click(function () {
$.ajax({
url: "http://127.0.0.1:8002/abc/",
dataType: "jsonp",
jsonp: "callback",
jsonpCallback: "rion2"
})
});
function rion2(res) {
console.log(res);
}
</script>
</body>
</html>
不过我们通常都会将回调函数写在success回调中:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>xyz</title>
</head>
<body>
<button id="b1">点我</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
$("#b1").click(function () {
$.ajax({
url: "http://127.0.0.1:8002/abc/",
dataType: "jsonp",
success: function (res) {
console.log(res);
}
})
})
</script>
</body>
</html>
####################################################
"""
三、JSONP JSONP是json用来跨域的一个东西。原理是通过script标签的跨域特性来绕过同源策略。(创建一个回调函数,然后在远程服务上调用这个函数并且将json数据形式作为参数传递,完成回调)。 JSONP的缺点
jsonp有个缺陷就是只能get
而且会把请求的内容发送到url中导致安全性极低 # script标签不受同源策略的影响,手动创建一个script标签,传递URL,同时传入一个回调函数的名字
# 服务器得到名字后,返回数据时会用这个函数名来包裹住数据,客户端获取到数据之后,立即把script标签删掉 """
#######################################
CORS解决跨域问题
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器发出XMLHttpRequest请求,从而解决AJAX只能同源使用的限制。
CORS简介
CORS需要浏览器和服务器同时支持。目前基本上主流的浏览器都支持CORS。所以只要后端服务支持CORS,就能够实现跨域。
简单请求和非简单请求介绍
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
一个请求需要同时满足以下两大条件才属于简单请求。
(1) 请求方法是以下三种方法之一:
HEAD
GET
POST (2)HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
简单请求的处理方式
在跨域场景下,当浏览器发送简单请求时,浏览器会自动在请求头中添加表明请求来源的 Origin 字段。
我们的后端程序只需要在返回的响应头中加上 Access-Control-Allow-Origin 字段,并且把该字段的值设置为 跨域请求的来源地址或简单的设置为 * 就可以了。
例如:我们可以在Django中间件中的process_response方法来给相应对象添加该字段。
from django.utils.deprecation import MiddlewareMixin
class CorsMiddleware(MiddlewareMixin):
    def process_response(self, request, response):
        # 给响应头加上 Access-Control-Allow-Origin 字段 并简单的设置为 *
        response['Access-Control-Allow-Origin'] = '*'
        return response
非简单请求的处理方式
我们开发中常用到的那些请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json的都是非简单请求。
对于非简单请求,浏览器通常都会在请求之前发送一次 OPTIONS 预检 请求。该请求会像后端服务询问是否允许从当前源发送请求并且询问允许的 请求方法 和 请求头字段。
举个例子:
我们前端使用axios向后端发送PUT请求,结果:失败,
解决办法也很简单,我们可以在后端简单的给响应对象添加上 常用请求方法(PUT、DELETE)的支持就可以了
在上面Django的中间件中添加如下代码:
from django.utils.deprecation import MiddlewareMixin
class CorsMiddleware(MiddlewareMixin):
    def process_response(self, request, response):
        # 给响应头加上 Access-Control-Allow-Origin 字段 并简单的设置为 *
        response['Access-Control-Allow-Origin'] = '*'
        if request.method == 'OPTIONS':
            # 允许发送 PUT 请求
            response['Access-Control-Allow-Methods'] = 'PUT, DELETE'
            # 允许在请求头中携带 Content-type字段,从而支持发送json数据
            response['Access-Control-Allow-Headers'] = 'Content-type'
        return response
################################################
使用django-cors-headers
我们这个中间件确实能解决目前的CORS跨域问题,但是我们的土方法肯定是不够严谨的,已经有人造好轮子-- django-cors-headers 了。
我们只需要安装这个包,然后按需要配置一下就可以了。 安装
pip install django-cors-headers
注册APP
INSTALLED_APPS = [
...
'app01.apps.App01Config',
'corsheaders', # 将 corsheaders 这个APP注册
] 添加中间件
必须放在最前面,因为要先解决跨域的问题。只有允许跨域请求,后续的中间件才会正常执行。
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', # 添加中间件
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
配置
你可以选择不限制跨域访问
CORS_ORIGIN_ALLOW_ALL = True
或者你可以选择设置允许访问的白名单
CORS_ORIGIN_ALLOW_ALL = False
CORS_ORIGIN_WHITELIST = (
# '<YOUR_DOMAIN>[:PORT]',
'127.0.0.1:8080'
)
################################################
{%csrf_token%}的作用
<form>
{%csrf_token%}
</form>
在django中我们需要在templates的form中加入{%csrf_token%}这串内容,
它的作用是当我们get表单页面时,服务器返回页面的同时也会向前端返回一串随机字符,
post提交时服务器会验证这串字符来确保用户是在服务端返回的表单页面中提交的数据,
防止有人通过例如jquery脚本向某个url不断提交数据,是一种数据提交的验证机制。
################################################
"""
四、CORS跨域
随着技术的发展,现在的浏览器可以主动支持设置从而允许跨域请求,即:跨域资源共享(CORS,Cross-Origin Resource Sharing),其本质是设置响应头,使得浏览器允许跨域请求。
2.cors:跨域资源共享
# 使用自定义的HTTP头部允许浏览器和服务器相互通信
# 1.如果是简单请求,直接设置允许访问的域名:
# 允许你的域名来获取我的数据
# response['Access-Control-Allow-Origin'] = "*"
# 2.如果是复杂请求,首先会发送options请求做预检,然后再发送真正的PUT/POST....请求
# 因此如果复杂请求是PUT等请求,则服务端需要设置允许某请求
# 如果复杂请求设置了请求头,则服务端需要设置允许某请求头
#简单请求:
# 一次请求
#非简单请求:
# 两次请求,在发送数据之前会先发一次请求用于做“预检”,
# 只有“预检”通过后才再发送一次请求用于数据传输。 """
#######################################
"""
五、JSONP和CORS的区别
JSONP:服务端不用修改,需要改前端。发jsonp请求
JSONP:只能发GET请求
CORS:前端的代码不用修改,服务端的代码需要修改。如果是简单请求的话在服务端加上一个响应头。
CORS:可以发任意请求
"""
#######################################
#######################################
#######################################
#######################################
demo1 8002端口
django框架进阶-解决跨域问题的更多相关文章
- django框架如何解决跨域问题
		跨域问题的由来 由于浏览器具有同源策略的限制. 限制:在发送Ajax请求时,如果当前浏览器的URL是a.com,页面中向b.com发送Ajax请求,请求可以正常访问,但数据回到浏览器时,浏览器会阻止. ... 
- Django后端彻底解决跨域问题
		最近在接一个前后端分离的项目,后端使用的django-restframework,前端使用的Vue.后端跑起来后,发现前端在访问后端API时出了了跨域的问题. 类似如下报错: 关于跨域问题,之前这篇文 ... 
- [django]django配合前端vue前后端联调,django服务端解决跨域(django-cors-headers)
		django内部csrf post提交数据解决 https://www.cnblogs.com/iiiiiher/articles/9164940.html 前端写了个页面,里面$.post发现403 ... 
- django之csrf_exempt解决跨域请求的问题
		一: from django.views.decorators.csrf import csrf_exempt # 获取微信返回的code信息 @csrf_exempt def wechat_auth ... 
- Django使用cors解决跨域问题
		1.安装Django-cors-headers模块 pip install django-cors-headers 2.配置settings.py文件 INSTALLED_APPS = [ ... ' ... 
- gin框架中间件解决跨域问题
		http://www.niu12.com/article/45// 初始化routerrouter := gin.New() router.Use(gin.Logger()) router.Use(g ... 
- Python之Flask和Django框架解决跨域问题,配合附加ajax和fetch等js代码
		Flask框架py解决跨域问题示例: # -*- coding: utf- -*- # by zhenghai.zhang from flask import Flask, render_templa ... 
- 解决跨域(CORS)问题
		为什么会有跨域问题 是因为浏览器的同源策略是对ajax请求进行阻拦了,但是不是所有的请求都给做跨域,像是一般的href属性,a标签什么的都不拦截 解决跨域问题的两种方式 JSONP 推荐参考 CO ... 
- 关于跨域介绍和djiago解决跨域问题
		什么是跨域? 跨域,指的是浏览器不能执行其他网站的脚本.它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制. 什么是同源策略? 同源策略又分为以下两种 DOM同源策略:禁止对不 ... 
随机推荐
- oracle(8)视图和查询数据库对象方法
			视图 view 视图是数据库的对象之一. 视图也叫做虚表,既虚拟表,本质是对应一条select 语句, select语句的结果集赋予一个名字就是视图的名字. 作用: 1.可以简化复杂的查询 2.可以限 ... 
- Arduino学习——u8glib库资料整理
			第一部分,u8glib标准语法格式: 本文使用的是DFRobot出品的LCD12864 Shield V1.0 端口占用情况: SPI Com: SCK = 13, MOSI = 11, CS = 1 ... 
- linux_ssh用户枚举猜测
			新建一个用户名txt文档,写入常用的用户名 msfconsole use auxiliary/scanner/ssh/ssh_enumusers3 
- golang实现单链表
			package main import "fmt" type Object interface{} type Node struct { data Object next *Nod ... 
- Java 使用控制台操作实现数据库的增删改查
			使用控制台进行数据库增删改查操作,首先创建一个Java Bean类,实现基础数据的构造,Get,Set方法的实现,减少代码重复性. 基本属性为 学生学号 Id, 学生姓名 Name,学生性别 Sex, ... 
- pinpoint 单机HBASE数据量过大问题解决
			Pinpoint接入业务监控后数据量大涨,平均每周Hbase数据增量35G左右,数据量太大,需要对数据进行定期清理,否则监控可用性降低. 操作步骤 查找出数据大的hbase表 [root@iZ28ov ... 
- iOS精美过度动画、视频会议、朋友圈、联系人检索、自定义聊天界面等源码
			iOS精选源码 iOS 精美过度动画源码 iOS简易聊天页面以及容联云IM自定义聊天页面的实现思路 自定义cell的列表视图实现:置顶.拖拽.多选.删除 SSSearcher仿微信搜索联系人,高亮搜索 ... 
- 2. Rabbitmq  php 安装 amqp 拓展
			记录一下 使用 rabbitmq 安装 amqp 拓展 环境 ubuntu 安装目录在 /user/local 下进行 1. 安装 wget apt-get update apt-get inst ... 
- 在项目中ES6语法转ES5语法  babel转码器
			es6 babel 安装以及使用 1,安装好node(需要使用npm包管理工具) 2,在本地项目路径下npm init,格式化成功后会在项目下生成一个配置文件package.json 3,本地安装 ... 
- priority_queue(优先队列)的用法(包括pbds)
			置顶!!! 有时候在定义的时候,不要把两个<>连在一起写,以免被编译器错误理解!!!! 头文件 #include <queue> queue的一般用法不再叙述 类型名 prio ... 
