zoukankan      html  css  js  c++  java
  • 关于跨域介绍和djiago解决跨域问题

    什么是跨域?

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

    什么是同源策略?

    同源策略又分为以下两种

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



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

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

    同源策略(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文件:

    from django.utils.deprecation import MiddlewareMixin
    
    
    class MyTest(MiddlewareMixin):
        def process_response(self, request, response):
            response['Access-Control-Allow-Origin'] = '*'
            return response

    其他字段:

    方案二:

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

    1. 安装

    pip install django-cors-headers

    2.添加到setting的app中

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

     3.添加中间件

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

    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>
    
    

    
    
  • 相关阅读:
    几款JS地图插件比较
    Objective-C ,ios,iphone开发基础:多个视图(view)之间的切换2,使用导航栏控制,以及视图之间传值。
    学习嵌入式—导火线
    Linux MySQL 5.1源码安装
    QT 一些非常常用的操作
    QT 下把编辑框内的中文字符转换为 char*
    delphi datasnap 心跳包
    ddd
    Qt 如何处理密集型耗时的事情(频繁调用QApplication::processEvents)
    Python基础-输入输出(IO)
  • 原文地址:https://www.cnblogs.com/Rivend/p/11750162.html
Copyright © 2011-2022 走看看