同源策略:JSONP和CORS

Posted 柚柚柚切克闹

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了同源策略:JSONP和CORS相关的知识,希望对你有一定的参考价值。

什么是同源策略:

  同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。

  但是两个源在达成共识后,需要相互传递数据就需要避开这个问题。所有就要使用JSONP和CORS

  更多参考:点击

  Jquery中JSONP官方文档:点击

一、JSONP处理非同源拦截

基于Ajax的JSONP使用方法:一般情况下使用ajax提供的方法即可。

  传递数据方的地址:127.0.0.1:8000/jsonp

  传递数据方的视图函数:

def test_jsonp(request):
    new_dict = {
        \'state\': 1,
        \'data\': [0, 1, 2, 3]
    }

    func = request.GET.get(\'dingding\') # 127.0.0.1:8000/jsonp/?dingding=回调函数名,如果接收数据方不指定jsonpCallback参数,回调函数默认为success

    return HttpResponse(\'{}("{}")\' .format(func, new_dict))  # 需要把回调函数名和数据一起传过去,如果接收数据方不指定jsonpCallback参数,默认正常执行success

  接收数据方的地址:127.0.0.1:8005

  接收数据方的ajax:

    $(\'#jsonp\').on(\'click\', function () {
        $.ajax({
            url:\'http://127.0.0.1:8000/jsonp/\',
            type:\'get\',
            dataType:\'jsonp\',  //指定接收jsonp格式
            jsonp:\'dingding\',  // 即http://127.0.0.1:8000/jsonp/?dingding=回调函数名,默认为callback
            {#jsonpCallback:\'test\', //指定回调函数为test,回调函数名为接收数据后需要执行的函数,默认执行success#}
            {#data:{"csrfmiddlewaretoken": $("[name = \'csrfmiddlewaretoken\']").val()}, //一定是get请求,不需要csrf#}
            success:function (data) {
                console.log(data)
            }
        })
        function test(data) {
            console.log(data)
        }
    })

  一个简单的jsonp发送和接收数据就已经完成,需要注意:

      1、Ajax提供简洁的方法,它的内部实现其实是函数的调用与DOM操作一个script标签

      2、使用Ajax提供的方法必须指定dataType参数为jsonp

      3、jsonp参数指定get请求发送的数据,例如http://127.0.0.1:8000/jsonp/?dingding=,默认为callback

      4、jsonpCallback指定回调函数名,不指定则默认执行success

jQuery中同样有专门的方法实现jsonp,但是不能指定回调函数:

  $("#jsonp").click(function () {
    $.getJSON("http://127.0.0.1:8000/jsonp/?callback=?", function (res) {
      console.log(res);
    })
  });

 

二、CORS彻底解决跨域:

  CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器发出XMLHttpRequest请求,从而解决AJAX只能同源使用的限制。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

    添加中间件:必须放在最前面,因为要先解决跨域的问题。只有允许跨域请求,后续的中间件才会正常执行。

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\',
]

    中间件对所有视图有效,当需要为单独的一个视图添加时,只需在视图函数添加Access-Control-Allow-Origin字段即可

def index_jsonp(request):
    new_dict = {
        \'state\': 1,
        \'data\': [0, 1, 2, 3]
    }

    func = request.GET.get(\'callback\')

    response = HttpResponse(\'{}({})\' .format(func, json.dumps(new_dict)))
    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\'
)

  更多详细配置详细请查看django-cors-headers项目    

以上是关于同源策略:JSONP和CORS的主要内容,如果未能解决你的问题,请参考以下文章

当 JSONP 和 CORS 等变通方法存在时,为啥浏览器会有同源策略?

JSONP跨域和CORS跨域的区别

Jsonp&Cors跨域(同源策略跨域劫持漏洞)

跨域 JSONP, CORS

跨域(jsonp cors)

浏览器的同源策略和CORS跨域