关于跨域介绍和djiago解决跨域问题 什么是跨域? 什么是同源策略? 为什么我们需要跨域限制? 解决跨区: Django 解决跨域问题  解决跨域问题jsonnp  script简单版原理   jsonp跨域请求:

跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制

什么是同源策略?

同源策略又分为以下两种

  • DOM同源策略:禁止对不同源页面DOM进行操作。这里主要场景是iframe跨域的情况,不同域名的iframe是限制互相访问的。
    XmlHttpRequest同源策略:禁止使用XHR对象向不同源的服务器地址发起HTTP请求。
  • 只要协议、域名、端口有任何一个不同,都被当作是不同的域,之间的请求就是跨域操作



所谓同源是指,域名,协议,端口 均相同

关于跨域介绍和djiago解决跨域问题
什么是跨域?
什么是同源策略?
为什么我们需要跨域限制?
解决跨区:
Django 解决跨域问题
 解决跨域问题jsonnp  script简单版原理
 
jsonp跨域请求:

如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。

关于跨域介绍和djiago解决跨域问题
什么是跨域?
什么是同源策略?
为什么我们需要跨域限制?
解决跨区:
Django 解决跨域问题
 解决跨域问题jsonnp  script简单版原理
 
jsonp跨域请求:

同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,

则浏览器的正常功能可能都会受到影响。可以说 Web 是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。

客户端JavaScript程序。虽然是一种解释性的脚本语言,JavaScript其实是无比强大的,原则上来讲它可以做任何事。

但是在能够在JavaScript脚本并不都是值得信赖的,所以浏览器必须对JavaScript的执行作相应的限制。

比如我们ajax可以随便发送一个请求就能取到数据,修改数据,那我们每个人都是黑客了。很多敏感信息就泄露。

但是对src等请求是没有限制的。

为什么我们需要跨域限制?

主要是出于安全的考虑
AJAX同源策略主要用来防止CSRF攻击。如果没有AJAX同源策略,相当危险,

我们发起的每一次HTTP请求都会带上请求地址对应的cookie,那么可以做如下攻击:

  • 用户登录了自己的银行页面 http://mybank.comhttp://mybank.com向用户的cookie中添加用户标识。
  • 用户浏览了恶意页面 http://evil.com。执行了页面中的恶意AJAX请求代码。
  • http://evil.comhttp://mybank.com发起AJAX HTTP请求,请求会默认把http://mybank.com对应cookie也同时发送过去。
  • 银行页面从发送的cookie中提取用户标识,验证用户无误,response中返回请求数据。此时数据就泄露了。
  • 而且由于Ajax在后台执行,用户无法感知这一过程。

DOM同源策略也一样,如果iframe之间可以跨域访问,可以这样攻击:

  1. 做一个假网站,里面用iframe嵌套一个银行网站 http://mybank.com
  2. 把iframe宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。
  3. 这时如果用户输入账号密码,我们的主网站可以跨域访问到http://mybank.com的dom节点,就可以拿到用户的输入了,那么就完成了一次攻击。



解决跨区:

合理的跨域访问---CORS

 CORS:”跨域资源共享”(Cross-origin resource sharing),这是一个W3C标准
CORS需要浏览器和服务器同时支持。

目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,
代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信
 

CORS可以分成两种:

1、简单请求

        同时满足两大条件,就属于简单请求
  • 请求方式:GET、POST、HEAD(注:什么是HEAD请求?HEAD请求和GET本质是一样的,但是HEAD请求不含数据,只有HTTP头部信息)
  • HTTP头部信息不超过一下几种字段:
  • 无自定义头部字段、
  • Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type(只有三个值application/x-www-form-urlencoded、multipart/form-data、text/plain)


2、复杂请求

非简单请求
  • 请求方式:PUT、DELETE
  • 自定义头部字段
  • 发送json格式数据
  • 正式通信之前,浏览器会先发送OPTION请求,进行预检,这一次的请求称为“预检请求”
  • option是什么?
  •         ( OPTION请求询问服务器,你支持哪种请求方法啊?      服务端响应支持GET,POST和OPTION等。。
  • 服务器成功响应预检请求后,才会发送真正的请求,并且携带真实数据

简单请求

一个简单的请求大致如下:

HTTP方法是下列之一

HEAD
GET
POST

HTTP头信息不超出以下几种字段

Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type,但仅能是下列之一
application/x-www-form-urlencoded
multipart/form-data
text/plain

任何一个不满足上述要求的请求,即被认为是复杂请求。一个复杂请求不仅有包含通信内容的请求,同时也包含预请求(preflight request)。

简单请求的部分响应头及解释如下:

Access-Control-Allow-Origin(必含)- 不可省略,否则请求按失败处理。该项控制数据的可见范围,如果希望数据对任何人都可见,可以填写"*"
Access
-Control-Allow-Credentials(可选) – 该项标志着请求当中是否包含cookies信息,只有一个可选值:true(必为小写)
。如果不包含cookies,请略去该项,而不是填写false。这一项与XmlHttpRequest2对象当中的withCredentials属性应保持一致,
即withCredentials为true时该项也为true;withCredentials为false时,省略该项不写。反之则导致请求失败。
Access
-Control-Expose-Headers(可选) – 该项确定XmlHttpRequest2对象当中getResponseHeader()方法所能获得的额外信息。
通常情况下,getResponseHeader()方法只能获得如下的信息: Cache-Control Content-Language Content-Type Expires Last-
Modified Pragma 当你需要访问额外的信息时,就需要在这一项当中填写并以逗号进行分隔

复杂请求

复杂请求表面上看起来和简单请求使用上差不多,但实际上浏览器发送了不止一个请求
其中最先发送的是一种"预请求",此时作为服务端,也需要返回"预回应"作为响应。
预请求实际上是对服务端的一种权限请求只有当预请求成功返回,实际请求才开始执行。
 
预请求以OPTIONS形式发送,当中同样包含域,并且还包含了两项CORS特有的内容:
 
Access-Control-Request-Method  
该项内容是实际请求的种类,可以是GET、POST之类的简单请求,也可以是PUT、DELETE等等。
Access
-Control-Request-Headers
该项是一个以逗号分隔的列表,当中是复杂请求所使用的头部。
显而易见,这个预请求实际上就是在为之后的实际请求发送一个权限请求
在预回应返回的内容当中,服务端应当对这两项进行回复,以让浏览器确定请求是否能够成功完成。

多了一个OPTIONS的预检请求,例如

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...`

服务器需要对预检请求进行回应

服务器收到"预检"请求以后,检查了OriginAccess-Control-Request-MethodAccess-Control-Request-Headers字段以后,

确认允许跨源请求,就可以做出回应。

> HTTP/1.1 200 OK
> Date: Mon, 01 Dec 2008 01:15:39 GMT
> Server: Apache/2.0.61 (Unix)
> Access-Control-Allow-Origin: http://api.bob.com
> Access-Control-Allow-Methods: GET, POST, PUT
> Access-Control-Allow-Headers: X-Custom-Header
> Content-Type: text/html; charset=utf-8
> Content-Encoding: gzip
> Content-Length: 0
> Keep-Alive: timeout=2, max=100
> Connection: Keep-Alive
> Content-Type: text/plain
 
Access-Control-Allow-Origin(必含)
– 和简单请求一样的,必须包含一个域。
Access
-Control-Allow-Methods(必含)
– 这是对预请求当中Access-Control-Request-Method的回复,
这一回复将是一个以逗号分隔的列表。尽管客户端或许只请求某一方法,但服务端仍然可以返回所有允许的方法,以便客户端将其缓存。
Access
-Control-Allow-Headers(当预请求中包含Access-Control-Request-Headers时必须包含)

这是对预请求当中Access-Control-Request-Headers的回复,和上面一样是以逗号分隔的列表,可以返回所有支持的头部。
这里在实际使用中有遇到,所有支持的头部一时可能不能完全写出来,而又不想在这一层做过多的判断,没关系,
事实上通过request的header可以直接取到Access-Control-Request-Headers,直接把对应的value设置到Access-Control-Allow-Headers即可。
Access
-Control-Allow-Credentials(可选)
– 和简单请求当中作用相同。
Access
-Control-Max-Age(可选)
– 以秒为单位的缓存时间。预请求的的发送并非免费午餐,允许时应当尽可能缓存
 
 
 
如果浏览器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。
这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。控制台会打印出如下的报错信息。

XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.

(1)Access-Control-Allow-Methods 授权请求的方法(GET, POST, PUT, DELETE,OPTIONS等)
该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。

注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。

(2)Access-Control-Allow-Headers 
如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。

它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。

(3)Access-Control-Allow-Credentials

该字段与简单请求时的含义相同。

(4)Access-Control-Max-Age

该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),

即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。

(5)Access-Control-Allow-Origin:指定授权访问的域

response.addHeader("Access-Control-Allow-Origin", "http://192.168.56.130");


 浏览器的正常请求和回应

一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。
服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。
下面是"预检"请求之后,浏览器的正常CORS请求。
PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

上面头信息的Origin字段是浏览器自动添加的。

下面是服务器正常的回应。

Access-Control-Allow-Origin: http://api.bob.com
> Content-Type: text/html; charset=utf-8

own

基于django的cors跨域例子:

 在服务端的设置:

def service(request):
    # 你需要跨域传的数据
    info = {"name": "egon", "age": 34, "prive": 200}

    response = HttpResponse(json.dumps(info))
    # 解决跨域问题 * 表示允许所有的跨域请求
    response["Access-Control-Allow-Origin"] = "*"
    # 在djiango 2.26版本中 经过我实验下面这个方法没用
    # response['Access-Control-Allow-Origin'] = "http://127.0.0.1:8006/"
    return response

Django 解决跨域问题

方案一:

在app下创建一个middlewares的py文件:

关于跨域介绍和djiago解决跨域问题
什么是跨域?
什么是同源策略?
为什么我们需要跨域限制?
解决跨区:
Django 解决跨域问题
 解决跨域问题jsonnp  script简单版原理
 
jsonp跨域请求:

from django.utils.deprecation import MiddlewareMixin


class MyTest(MiddlewareMixin):
    def process_response(self, request, response):
        response['Access-Control-Allow-Origin'] = '*'
        return response

关于跨域介绍和djiago解决跨域问题
什么是跨域?
什么是同源策略?
为什么我们需要跨域限制?
解决跨区:
Django 解决跨域问题
 解决跨域问题jsonnp  script简单版原理
 
jsonp跨域请求:

其他字段:

关于跨域介绍和djiago解决跨域问题
什么是跨域?
什么是同源策略?
为什么我们需要跨域限制?
解决跨区:
Django 解决跨域问题
 解决跨域问题jsonnp  script简单版原理
 
jsonp跨域请求:

方案二:

django 使用django-cors-headers 解决跨域问题(待测试)

1. 安装

pip install django-cors-headers

2.添加到setting的app中

INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)

关于跨域介绍和djiago解决跨域问题
什么是跨域?
什么是同源策略?
为什么我们需要跨域限制?
解决跨区:
Django 解决跨域问题
 解决跨域问题jsonnp  script简单版原理
 
jsonp跨域请求:

 3.添加中间件

MIDDLEWARE = [  # Or MIDDLEWARE_CLASSES on Django < 1.10
    ...
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    ...
]

关于跨域介绍和djiago解决跨域问题
什么是跨域?
什么是同源策略?
为什么我们需要跨域限制?
解决跨区:
Django 解决跨域问题
 解决跨域问题jsonnp  script简单版原理
 
jsonp跨域请求:

4、setting下面添加下面的配置

CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_WHITELIST = (
    '*'
)
CORS_ALLOW_METHODS = (
    'DELETE',
    'GET',
    'OPTIONS',
    'PATCH',
    'POST',
    'PUT',
    'VIEW',
)

CORS_ALLOW_HEADERS = (
    'XMLHttpRequest',
    'X_FILENAME',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
    'Pragma',
)

 解决跨域问题jsonnp  script简单版原理

http://127.0.0.1:8006/ 端口(我是向8007跨域请求端)

前端页面:

<button class="get_service">洗剪吹</button>

</body>

<script>
    // 跨域端口返回get_jsonp_data函数调用,将值以arg形式传进来。
    function get_jsonp_function(arg) {
        alert(arg);
        console.log(typeof arg);
        var data = JSON.parse(arg);
        console.log(data);
        console.log(typeof data);
    }
</script>


<script>
    //点击向跨域端口发送一次service请求

    function get_jsonp_data(url) {
        //创建一个script标签
        var ele_svript = $("<script>");
        //添加一个src属性
        ele_svript.attr("src", url);
        ele_svript.attr("id", "jsonp");
        // 添加到body文档树后面
        $("body").append(ele_svript);
//将多余的文档删除 $("#jsonp").remove() } </script> <script> $(".get_service").on("click", function () { //以另一端传过来的接口?callback= 自定义我们的函数名 get_jsonp_data("http://127.0.0.1:8007/service/?callback=get_jsonp_function"); }) </script> </html>

 http://127.0.0.1:8007/ 端口(我是服务端)

view视图端

from django.shortcuts import render, HttpResponse
import json

# Create your views here.

def index(request):
    return render(request, "index.html")


def service(request):

    # 你需要跨域传的数据
    info = {"name": "egon", "age": 34, "prive": 200}

    # 前端自己创建函数名,我只要给那边一个callback接口就行
    func = request.GET.get("callback")

    return HttpResponse("%s('%s')" % (func, json.dumps(info),))

 

jsonp跨域请求:

什么是jsonp?

JSON & JSONP:JSON 是一种基于文本的数据交换方式,或者叫做数据描述格式

JSONP是资料格式JSON的一种“使用模式”,可以让网页从别的网域要资料,由于同源策略,

一般来说位于server1.example.com的网页无法与不是 server1.example.com的服务器沟通,而HTML的script元素是一个例外。

利用script元素的这个开放策略,网页可以得到从其他来源动态产生的JSON资料,而这种使用模式就是所谓的JSONP。

缺点:

1.JSONP只能实现GET请求。

2.JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS。

3.也就是说在浏览器支持cors的情况下 尽量使用cors方式。

JSONP高级版:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--配置手机端适应-->
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <!--配置css文件 核心CSS样式压缩文件-->
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/font-awesome-4.7.0/css/font-awesome.css">
    <!--配置jQuery-->
    <script src="/static/bootstrap/jQuery.js"></script>
    <!--配置 核心Boot script JS压缩文件-->
    <script src="/static/bootstrap/js/bootstrap.min.js"></script>

</head>
<body>
<h3>Idext</h3>

<button class="get_service">洗剪吹</button>


</body>

<script>
    // 跨域端口返回get_jsonp_data函数调用,将值以arg形式传进来。
    function get_jsonp_function(arg) {
        alert(arg);
        console.log(typeof arg);
        var data = JSON.parse(arg);
        console.log(data);
        console.log(typeof data);
    }
</script>

<script>
    //终极模式
    $(".get_service").on("click", function () {
        $.ajax({
            url: "http://127.0.0.1:8007/service/",
            type: "get",
            dataType: "jsonp", //伪造ajax  基于script
            jsonpCallback:"get_jsonp_function", //服务端需要传并且客户端需要执行的函数,
            success: function (data) {     // 数据也可以从success传进来并进行数据操作


            }
        })
    })
    
</script>

</html>

服务端:

from django.shortcuts import render, HttpResponse
import json


# Create your views here.

def index(request):
    return render(request, "index.html")


def service(request):
    # 你需要跨域传的数据
    info = {"name": "egon", "age": 34, "prive": 200}

    # 可以让跨区哪一步自己创建函数名,我只要给那边一个callback接口就行
    func = request.GET.get("callback")

    return HttpResponse("%s('%s')" % (func, json.dumps(info),))


一个跨域的实战列子:
<script>
    //示例 
    $(".get_service").on("click", function () {
        $.ajax({
            url: "http://www.jxntv.cn/data/jmd-jxtv2.html",
            type: "get",
            dataType: "jsonp", //伪造ajax  基于script
            jsonp: "callbacks", //需要发送伪造的服务端的函数名称
            jsonpCallback:"list" ,//服务端需要并执行的函数,服务端以经定好了list所以必须要以list接受
            success: function (data) {
                console.log(data);
                //数据处理

                var html="";
                // each 是jquery中的循环
                $.each(data.data,function (index,weekday) {
                    console.log(weekday); // {week: "周日", list: Array(19)}
                    // 我自定义了一个p标签,并且取出了字段中week的值。
                    html+='<p>'+weekday.week+'<p>'; // 加等于的意思就是就是叠加。

                    $.each(weekday.list, function (j,show) {
                        html+='<a href='+show.link+'>'+show.name+'</a>'

                    })

                });
                //找到body添加到后面
                $("body").append(html)

            }
        })
    })

</script>

关于跨域介绍和djiago解决跨域问题
什么是跨域?
什么是同源策略?
为什么我们需要跨域限制?
解决跨区:
Django 解决跨域问题
 解决跨域问题jsonnp  script简单版原理
 
jsonp跨域请求: