#######################################

"""
一、为什么会有跨域问题? 是因为浏览器的同源策略是对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框架进阶-解决跨域问题的更多相关文章

  1. django框架如何解决跨域问题

    跨域问题的由来 由于浏览器具有同源策略的限制. 限制:在发送Ajax请求时,如果当前浏览器的URL是a.com,页面中向b.com发送Ajax请求,请求可以正常访问,但数据回到浏览器时,浏览器会阻止. ...

  2. Django后端彻底解决跨域问题

    最近在接一个前后端分离的项目,后端使用的django-restframework,前端使用的Vue.后端跑起来后,发现前端在访问后端API时出了了跨域的问题. 类似如下报错: 关于跨域问题,之前这篇文 ...

  3. [django]django配合前端vue前后端联调,django服务端解决跨域(django-cors-headers)

    django内部csrf post提交数据解决 https://www.cnblogs.com/iiiiiher/articles/9164940.html 前端写了个页面,里面$.post发现403 ...

  4. django之csrf_exempt解决跨域请求的问题

    一: from django.views.decorators.csrf import csrf_exempt # 获取微信返回的code信息 @csrf_exempt def wechat_auth ...

  5. Django使用cors解决跨域问题

    1.安装Django-cors-headers模块 pip install django-cors-headers 2.配置settings.py文件 INSTALLED_APPS = [ ... ' ...

  6. gin框架中间件解决跨域问题

    http://www.niu12.com/article/45// 初始化routerrouter := gin.New() router.Use(gin.Logger()) router.Use(g ...

  7. Python之Flask和Django框架解决跨域问题,配合附加ajax和fetch等js代码

    Flask框架py解决跨域问题示例: # -*- coding: utf- -*- # by zhenghai.zhang from flask import Flask, render_templa ...

  8. 解决跨域(CORS)问题

    为什么会有跨域问题 是因为浏览器的同源策略是对ajax请求进行阻拦了,但是不是所有的请求都给做跨域,像是一般的href属性,a标签什么的都不拦截 解决跨域问题的两种方式 JSONP   推荐参考 CO ...

  9. 关于跨域介绍和djiago解决跨域问题

    什么是跨域? 跨域,指的是浏览器不能执行其他网站的脚本.它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制. 什么是同源策略? 同源策略又分为以下两种 DOM同源策略:禁止对不 ...

随机推荐

  1. 每天一点点之vue框架开发 - 数据渲染-for循环中动态设置页面背景色

    实现方式很简单,在属性前加:,表示绑定 :style="{'background':item.bgColor} 代码样例: <li v-for="item in laber_ ...

  2. 编写软件动态加载NT式驱动

    NT式设备驱动程序的动态加载主要是由服务控制管理程序(Service Control Manager,即SCM)系统组件来完成的. Windwos服务可以在系统启动时加载,用户也可以按需在服务控制平台 ...

  3. (转)mysql语句

    一.基础 1.说明:创建数据库 CREATE DATABASE database-name 2.说明:删除数据库 drop database dbname 3.说明:备份sql server --- ...

  4. 72)MFC测试动态共享库

    动态共享库: 首先我建立一个新的动态库: 然后不选择空项目了,因为我们普通的cpp文件 入口是main  win32入口是winmain  那么这个动态库的入口在哪里  我们就是为了看一看: 出来这样 ...

  5. UML-架构分析-架构原则

    1.高内聚 2.低耦合 3.防止变异(间接性等) 4.关注点分离 方法1: 事物模块化,封装到单独的子系统中 方法2: 装饰者模式 方法3: 面向方面(AOP)

  6. 2019年阿里java面试题

    一.JVM与性能优化 描述一下 JVM 加载 Class 文件的原理机制? 什么是类加载器? 类加载器有哪些? 什么是tomcat类加载机制? 类加载器双亲委派模型机制? Java 内存分配? Jav ...

  7. Jetson TX2入门学习之Ubuntu默认密码

    在使用TX2开发板时进行软件更新时需要身份验证,TX2默认有两个登录身份,一个是ubuntu 一个是nvidia 登录其中的哪一个都可以更新   两个身份的密码和登录名是一样的用户:ubuntu 密码 ...

  8. sqlserver2008的sql语句支持的最大长度

    想写一个sql语句,很长,主要是in后跟着无数个用户ID,(虽然实现方式很低级,但是还是凑合着用吧) 不知道sql最大长度是多少,看了 SQL Server 的最大容量规范,写的是 包含 SQL 语句 ...

  9. 吴裕雄--天生自然 JAVA开发学习:String 类

    public class StringDemo{ public static void main(String args[]){ char[] helloArray = { 'r', 'u', 'n' ...

  10. PAT Advanced 1085 Perfect Sequence (25) [⼆分,two pointers]

    题目 Given a sequence of positive integers and another positive integer p. The sequence is said to be ...