Loading

CORS跨站请求

  • 跨域的三大分类

    • xss: 跨站脚本攻击
    • csrf: 跨站请求伪造
    • cors: 跨域资源共享
  • 同源策略

    同源策略是浏览器的安全策略, 如果浏览器对javascript没有同源策略的保护, 那么一些重要的机密网站将会很危险.
    
    同源策略: 不允许不同域之间直接通信直接通信.
    请求的url地址, 必须与浏览器上的url地址处于同域上, 也就是域名, 端口, 协议相同.
    
    比如:我在本地上的域名是127.0.0.1:8000, 请求另外一个域名: 127.0.0.1:8001
    浏览器上就会报错,.
    
  • 跨站请求测试

    • 项目1(端口号为8008)

      image

      # 路由层
      from django.contrib import admin
      from django.urls import path, re_path
      from app01 import views
      
      urlpatterns = [
          path('admin/', admin.site.urls),
          re_path(r'^get_data/', views.get_data)
      ]
      
      # 视图层
      from django.http import JsonResponse
      
      # Create your views here.
      
      def get_data(request):
          print(f'{request.method} 请求访问 get_data')
          return JsonResponse({'k1': 'v1'})
      

      修改端口号为8008

      image

      启动项目, 浏览器访问 http://127.0.0.1:8008/get_data/

      image

    • 项目2 (端口号为8000)

      image

      # 路由层
      from django.contrib import admin
      from django.urls import path, re_path
      
      from app01 import views
      urlpatterns = [
          path('admin/', admin.site.urls),
          re_path(r'^index/', views.index)
      ]
      
      # 视图层
      from django.shortcuts import render
      
      
      # Create your views here.
      def index(request):
          return render(request, 'index.html')
      
      模板层代码
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>客户端</title>
          <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
      </head>
      <body>
      <button onclick="click1">发送请求</button>
      </body>
      
      <script>
          function click1() {
              $.ajax({
                  url: 'http://127.0.0.1:8008/get_data/',
                  type: 'get',
                  success: function (args) {
                      console.log(args)
                  }
              })
          }
      </script>
      </html>
      
      1. 先启动server项目, 在启动client项目, 
      2. 浏览器访问 https://127.0.0.1:8000/index/
      3. 点击按钮向https://127.0.0.1:8008/get_data/
      4. 报错已拦截跨源请求, 同源策略禁止读取... 原因:CORS 头缺少 'Access-Control-Allow-Origin'
      5. 可以看到服务端是收到了请求, 并返回了数据, 最后被浏览器拦截了	
      

      image

  • CORS简单/非简单请求

    只要同时满足以下两大条件,就属于简单请求, 凡是不同时满足下面两个条件, 就属于非简单请求.

    • 请求方法是以下三种方法之一:
      • HEAD
      • GET
      • POST
    • HTTP的头信息是以下几种字段:
      • Accept: 发送端(客户端)希望接受的数据类型.
      • Accept-Language: 希望采用的语言或语言组合.
      • Content-Language: 用来表示报文体(实体数据)使用的语言(ch,fr,en,ja等).
      • Last-Event-ID: 标识最后一次接受的event id.
      • Content-Type: 发送端(客户端|服务器)发送的实体数据的数据类型.
        默认只限于三个值application/x-www-form-urlencoded, multipart/form-data, text/plain

    浏览器对这两种请求的处理, 是不一样的.

    • 简单请求:只会发送一次请求
      • 为返回数据对象时, 在响应头中添加,Access-Control-Allow-Origin(访问控制允许源)的信息则可以资源共享.
    • 非简单请求: 会发送两次请求, 第一次发送OPTIONS请求用于做预检,
      • 预检请求时, 允许请求方式则需服务器设置响应头: Access-Control-Request-Method
      • 预检请求时, 允许请求头则需服务器设置响应头:Access-Control-Request-Headers (允许额外的头或数据类型)
      • 才会再以次发送请求用于数据传输。
        # 本质
        跨域资源共享, 本质就是在响应头部加入允许, 允许某些域, 允许某些头, 添加的域和头就能正常访问.
        简单请求需要允许访问的域,
        非简单请需要先设置允许某些头. 再设置域.
        
    • 简单请求资源共享

      在项目1的视图函数中, 返回数据对象的时候在响应头中添加Access-Control-Allow-Origin的信息.
      HttpResponse[key] = value 在响应头中添加数据
      Access-Control-Allow-Origin为键, 允许跨站获取资源的地址为值.
      
      from django.shortcuts import render
      from django.http import JsonResponse
      
      
      # Create your views here.
      
      def get_data(request):
          print(f'{request.method} 请求访问 get_data')
          response = JsonResponse({'k1': 'v1'})
      
          # * 代表允许所有求访问
          # response['Access-Control-Allow-Origin'] = '*'
          # 单独配置
          response['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8000'
      
          return response
      
      image
      使用使用rest_farmework的Response返回数据, 
      则为Response的headers响应头参数设置{'Access-Control-Allow-Origin': '*'}
      
    • 非简单请求资源共享

      • 修改项目2模板文件, 提交json格式的数据到后端
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>客户端</title>
            <script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
        </head>
        <body>
        <button onclick="click1()">发送json数据</button>
        </body>
        
        <script>
            function click1() {
                $.ajax({
                    url: 'http://127.0.0.1:8008/get_data/',
                    type: 'post',
                    contentType: 'application/json',
                    data: JSON.stringify({'k1': 'v1'}),
                    success: function (args) {
                        console.log(args)
                    }
                })
            }
        </script>
        </html>
        
      • 将项目1的csrf检验关闭
        MIDDLEWARE = [
            ...
            # 'django.middleware.csrf.CsrfViewMiddleware',
            ...
        
      • 先启动项目1, 在启动项2, 浏览器访问 127.0.0.1:8000/indiex/
        image
      • 修改项目1的视图函数
        def get_data(request):
            print(f'{request.method} 请求访问 get_data',)
            response = JsonResponse({'k1': 'v1'})
        
            # 如果是非简单请求发送的是OPTIONS请求, 在响应中添加 访问控制允许标头的信息
            if request.method == 'OPTIONS':
                response['Access-Control-Allow-Headers'] = 'Content-Type'
            response['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8000'
        
            return response
        
      • 先启动项目1, 在启动项2, 浏览器访问 127.0.0.1:8000/indiex/
        image
  • 自定制中间件中实现资源共享

    • 新建CorsMiddleware Cors中间件

      # utils文件内
      from django.utils.deprecation import MiddlewareMixin
      
      
      class CorsMiddleware(MiddlewareMixin):
          def process_response(self, request, response):
              if request.method == 'OPTIONS':
                  # 允许头的之后 Content-Type的不在限制
                  response['Access-Control-Allow-Headers'] = 'Content-Type'
              response['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8000'
      
              return response
      
    • 将自定义中间件添加到配置文件中

      MIDDLEWARE = [
          'utils.CorsMiddleware.CorsMiddleware',
          ...
      
    • 将视图函数中的Cors配置删除

      def get_data(request):
          print(f'{request.method} 请求访问 get_data',)
          response = JsonResponse({'k1': 'v1'})
          return response
      
    • 测试

      先启动项目1, 在启动项2, 浏览器访问 127.0.0.1:8000/indiex/

      image

  • django-cors-header模块

    • 安装模块

      默认安装最新的版本 需要django3.2版本及以上

      pip install django-cors-headers==2

    • 注册app

      django-cors-header是一个app, 需要注册在app列表中才能使用

      INSTALLED_APPS = [
          ...
          'corsheaders',
      ]
      
    • 添加中间件

      MIDDLEWARE = [
          'corsheaders.middleware.CorsMiddleware',
          # 注册自己的中间件
          # 'utils.CorsMiddleware.CorsMiddleware',
          ...
      
    • 配置文件setting.py中配置django-cors-header使用的参数

      # CORS_允许_凭据
      CORS_ALLOW_CREDENTIALS = True
      # CORS起源允许所有
      CORS_ORIGIN_ALLOW_ALL = True
      # 允许的方法
      CORS_ALLOW_METHODS = (
      	'DELETE',
      	'GET',
      	'OPTIONS',
      	'PATCH',
      	'POST',
      	'PUT',
      )
      # 允许的标头
      CORS_ALLOW_HEADERS = (
      	'authorization',
      	'content-type',
      )
      
      点击查看更多配置代码
      # CORS_允许_凭据
      CORS_ALLOW_CREDENTIALS = True
      # CORS起源允许所有
      CORS_ORIGIN_ALLOW_ALL = True
      # CORS起源白名单 需要https:// 开头
      CORS_ORIGIN_WHITELIST = ('地址1', '地址1')
      # 允许的方法
      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',
      )
      

      image

posted @ 2022-12-02 21:39  爱learn  阅读(54)  评论(0编辑  收藏  举报