JSONP和CORS跨域

同源策略

  • 同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。所以xyz.com下的js脚本采用ajax读取abc.com里面的文件数据是会被拒绝的。
  • 同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
  • 已经接收到了请求并返回了响应,是浏览器对非同源请求返回的结果做了拦截

一、源的定义

  • 如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源。
  • 下表给出了相对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)

二、不受同源策略限制的

  • 页面中的链接,重定向以及表单提交是不会受到同源策略限制的。
  • 2. 跨域资源的引入是可以的。但是js不能读写加载的内容。如嵌入到页面中的<script src="..."></script>,<img>,<link>,<iframe>等。

三、JSONP解决跨域问题

  • JSONP的原型:创建一个回调函数,然后在远程服务上调用这个函数并且将JSON 数据形式作为参数传递,完成回调。
  • 将JSON数据填充进回调函数,这就是JSONP的JSON+Padding的含义。
<!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>
html
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)))
view
<!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);
    })
  });

  //要注意的是在url的后面必须要有一个callback参数,这样getJSON方法才会知道是用JSONP方式去访问服务,
  // callback后面的那个?是jQuery内部自动生成的一个回调函数名。
</script>
</body>
</html>
jQuery中getJSON方法
<!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"  //如果服务端规定了回调函数名,可以使用$.ajax方法来实现:
    })
  });
  function rion2(res) {
    console.log(res);
  }


  //不过通常都会将回调函数写在success回调中:

   $("#b1").click(function () {
    $.ajax({
      url: "http://127.0.0.1:8002/abc/",
      dataType: "jsonp",
      success: function (res) {
        console.log(res);
      }
    })
  })

</script>
</body>
</html>
ajax

四、CORS解决跨域问题

  • CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器发出XMLHttpRequest请求,从而解决AJAX只能同源使用的限制。
  • CORS需要浏览器和服务器同时支持。目前基本上主流的浏览器都支持CORS。所以只要后端服务支持CORS,就能够实现跨域。
  • 浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

1、简单请求

一个请求需要同时满足以下两大条件才属于简单请求。

(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

2、简单请求的处理方式

在跨域场景下,当浏览器发送简单请求时,浏览器会自动在请求头中添加表明请求来源的 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
process_response

3、非简单请求的处理方式

  • 开发中常用到的那些请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json的都是非简单请求。
  • 对于非简单请求,浏览器通常都会在请求之前发送一次 OPTIONS 预检 请求。该请求会像后端服务询问是否允许从当前源发送请求并且询问允许的 请求方法 和 请求头字段。

解决办法:可以在后端简单的给响应对象添加上 常用请求方法(PUT、DELETE)的支持就可以了。

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
process_response

五、使用django-cors-headers跨域

上面中间件确实能解决目前的CORS跨域问题,但是不够严谨,已经有人造好轮子-- django-cors-headers 了。 更多详细配置详细请查看django-cors-headers项目

1.安装
    pip install django-cors-headers
2.注册APP

    INSTALLED_APPS = [
        ...
        'app01.apps.App01Config',
        'corsheaders',  # 将 corsheaders 这个APP注册
    ]
3.添加中间件
    必须放在最前面,因为要先解决跨域的问题。只有允许跨域请求,后续的中间件才会正常执行。

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

4.配置
    #可以选择不限制跨域访问
    CORS_ORIGIN_ALLOW_ALL = True

    # 或者可以选择设置允许访问的白名单
    CORS_ORIGIN_ALLOW_ALL = False
    CORS_ORIGIN_WHITELIST = (
        # '<YOUR_DOMAIN>[:PORT]',
        '127.0.0.1:8080'
    )
django-cors-headers 配置
原文地址:https://www.cnblogs.com/bubu99/p/11098479.html