zoukankan      html  css  js  c++  java
  • Luffy 跨域请求问题

    一:跨域请求

     

    0.跨域问题的出现

     

    问题出现:前后端来自同一个IP的不同端口

    一种奇葩的解决方法:开发的时候前后端分离,部署的时候不分离

     

    1.同源策略

    ① 简介

    同源策略,是浏览器为了保护用户信息安全的一种安全机制

    所谓的同源就是指代通信的两个地址(例如服务端接口地址与浏览器客户端页面地址)之间比较,是否协议、域名(IP)和端口相同

    不同源的客户端脚本[javascript]在没有得到服务端的明确授权的情况下,浏览器会拒绝显示服务端信息提供给前端ajax/axios

    前端地址:http://www.xuexianqi.top/index.html是否同源原因
    http://www.xuexianqi.top/user/login.html 协议、域名、端口相同
    http://www.xuexianqi.top/about.html 协议、域名、端口相同
    https://www.xuexianqi.top:443/user/login.html 协议不同 ( https和http )
    http:/www.xuexianqi.top:5000/user/login.html 端口 不同( 5000和80)
    http://doc.xuexianqi.top/user/login.html 域名不同 ( doc和www )
    http://doc.xuexianqi.com/user/login.html 域名不同 ( com和top )
     

    2.CORS(跨域资源共享)简介

    CORS是一个W3C标准,全称是"跨域资源共享",它允许浏览器向跨源的后端服务器发出ajax请求,从而克服了AJAX只能同源使用的限制。
    实现CORS主要依靠后端服务器中响应数据中设置响应头信息返回的

    CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。

    整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

    因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信

    容易混淆的点:

    • CORS:跨域资源共享
    • CSRF:跨站请求伪造
    • XSS:跨站脚本攻击
     

    3.CORS基本流程

    • 浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)
    • 浏览器发出CORS简单请求,只需要在头信息之中增加一个Origin字段
    • 浏览器发出CORS非简单请求,会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求(preflight)
    • 览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段
    • 只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
     

    4.解决跨域问题的3种方法

    • 前端解决(通过代理解决)
    • 自己解决(自己写代码)
    • 借助第三方模块(django-cors-headers)
     

    5.CORS两种请求详解

     

    只要同时满足以下两大条件,就属于简单请求

     

    ① 请求方法是以下三种方法之一

    • HEAD
    • GET
    • POST
     

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

    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
    凡是不同时满足上面两个条件,就属于非简单请求
     

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

     

    简单请求和非简单请求的区别

    • 简单请求:发1次请求
    • 非简单请求:发2次请求,在发送数据之前会先发1次请求用于做“预检”看后端是否允许,只有“预检”通过后才再发送1次请求用于数据传输。

    请求方式:OPTIONS

     

    预检:

    检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息

     

    如何预检:

    如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过

    • Access-Control-Request-Method

    如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过

    • Access-Control-Request-Headers
     

    二:解决跨域问题(服务端)

     

    简单请求

     

    1.原Django项目:apps/user/views.py

    from django.http import JsonResponse
    
    
    def test(request):
        obj = JsonResponse({'name': 'Darker', 'age': '18'})
        # 值针对简单请求
        obj['Access-Control-Allow-Origin'] = '*'    # 允许所有IP访问
        return obj

    2.原Django项目:apps/user/urls.py

    from django.urls import path
    from user import views
    
    urlpatterns = [
        path('test/', views.test),
    ]

    3.原Django项目:dev.py中注释掉CSRF

     

    4.再创建1个Django项目(用另外的端口)

    ① templates中创建index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    </head>
    <body>
    <button id="btn">点我</button>
    
    </body>
    <script>
        $('#btn').click(function () {
            $.ajax({
                url: 'http://127.0.0.1:8000/user/test/',
                method: 'get',
                success: function (data) {
                    console.log(data)
                }
            })
        })
    </script>
    </html>
    ② views.py
    from django.shortcuts import render
    
    
    def index(request):
        return render(request, 'index.html')
    ③ urls.py
    from django.urls import path
    from app01 import views
    
    urlpatterns = [
        path('test/', views.index),
    ]

     

    非简单请求

     

    原Django项目apps/user/views.py

    from django.http import JsonResponse
    
    
    def test(request):
        obj = JsonResponse({'name': 'Darker', 'age': '18'})
        if request.method == 'OPTIONS':
            obj['Access-Control-Allow-Headers'] = 'Content-Type,authorization'  # 或者填写 *
        obj['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8002'    # 8002端口是当前项目的
        return obj

    新Django项目templates/index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    </head>
    <body>
    <button id="btn">点我</button>
    
    </body>
    <script>
        $('#btn').click(function () {
            let obj = {name: 'Darker'}
            $.ajax({
                url: 'http://127.0.0.1:8000/user/test/',
                method: 'post',
                contentType: 'application/json',
                headers: {authorization: 'Darker'},
                data: JSON.stringify(obj),
                success: function (data) {
                    console.log(data)
                }
            })
        })
    </script>
    </html>

    3.中间件处理

    ① 在原Django项目的根路径创建mymiddle.py - 自定义中间件
    from django.utils.deprecation import MiddlewareMixin
    
    
    class CoreMiddle(MiddlewareMixin):
        def process_response(self, request, response):
            if request.method == 'OPTIONS':
                response['Access-Control-Allow-Headers'] = 'Content-Type, authorization'    # 如果是 * 就代表全部IP都可以访问
            response['Access-Control-Allow-Origin'] = '*'
            return response

    ② 在原Django项目的dev.py的中间件中添加自定义中间件

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'mymiddle.CoreMiddle',    # 这一句
        'django.middleware.common.CommonMiddleware',
        # 'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]

    ③ 原Django项目apps/user/views中替换成如下代码

    from django.http import JsonResponse
    
    
    def test(request):
        return JsonResponse({'name': 'Darker', 'age': '18'})

    三:解决跨域问题(第三方)

     

    1.后端配置

     

    ① 后端安装跨域模块

    pip install django-cors-headers

    ② 到dev.py中进行注册

    INSTALLED_APPS = (
        ...
        'corsheaders'
    )

    ③ 到dev.py中添加中间件

    MIDDLEWARE = [
        ...
        'corsheaders.middleware.CorsMiddleware',
        'django.middleware.common.CommonMiddleware',    # 这个是原本就存在的
        ...
    ]

    ④ 到dev.py中添加如下代码

    CORS_ALLOW_CREDENTIALS = True
    CORS_ORIGIN_ALLOW_ALL = True
    # CORS_ORIGIN_WHITELIST = (
    #     'http://127.0.0.1:8080',
    # )
    CORS_ALLOWED_ORIGINS_REGEXES=[
        r'^http://.*?$',
    ]
    # CORS_ORIGIN_REGEXES_WHITELIST = (
    #         r'^http://.*?$',
    # )
    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',
    )

    ⑤ 设置dev.pyALLOWED_HOSTS

    ALLOWED_HOSTS = ['*']

    2.后端测试

     

    ① (测试 - 后台)到apps/user/views中替换成如下代码

    from django.http import JsonResponse
    
    
    def test(request):
        return JsonResponse({'name': 'Darker', 'age': '18'})

    ② (测试 - 后台)到apps/user/urls中替换成如下代码

    from django.urls import path
    from user import views
    
    urlpatterns = [
        path('test/', views.test),
    ]

    ③ 启动项目

    python manage.py runserver 127.0.0.1:8000

    ④ 访问测试

     

    3.前端测试

     

    ① (测试 - 前台)App.vue 中换成如下代码

    <template>
      <div id="app">
        <router-view/>
        {{name}}
      </div>
    </template>
    
    <script>
    export default {
      data () {
        return {
          name: []
        }
      },
      mounted () {
        this.$axios.get(this.$settings.base_url + '/user/test/').then(res => {
          this.name = res.data
        })
      }
    }
    </script>

    ② 启动项目

    npm run serve

    4.测试效果

     

    四:解决跨域问题(前端)

     

    1.前端App.vue

    <template>
      <div id="home">
        <h1>我是主页</h1>
        <h2>{{info}}</h2>
      </div>
    </template>
    
    <script>
    export default {
      name: 'Home',
      data () {
        return {
          info: []
        }
      },
      mounted () {
        this.$axios.get('/moreClassicList?sortId=1&showType=3').then(res => {
          console.log(res.data)
        })
      }
    }
    </script>
    
    <style scoped>
    
    </style>

    2.前端项目根路径的vue.config.js

    const webpack = require("webpack");
    
    module.exports = {
      configureWebpack: {
        plugins: [
          new webpack.ProvidePlugin({
            $: "jquery",
            jQuery: "jquery",
            "window.jQuery": "jquery",
            "window.$": "jquery",
            Popper: ["popper.js", "default"]
          })
        ]
      },
      devServer: {
        proxy: {
          '/ajax': {
            target: 'https://m.maoyan.com/',
            changeOrigin: true
          },
          '/user': {
            target: 'http://127.0.0.1:8000',
            changeOrigin: true
          }
        }
      }
    };

  • 相关阅读:
    如何使用Tomcat
    Android推送通知指南(转)
    路由器
    供应链是什么意思
    c#打印(通过Word)
    RFID(电子标签、射频识别)技术在医疗行业中的应用
    无线数传DTU
    在C#中获取打印机的当前状态
    CCD是什么
    Failed to enable constraints. One or more rows contain values violating nonnull, unique, or foreignkey constraints.
  • 原文地址:https://www.cnblogs.com/ltyc/p/14179479.html
Copyright © 2011-2022 走看看