跨域请求问题解决

跨域请求

配置参考

一 同源策略

同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现

请求的url地址,必须与浏览器上的url地址处于同域上,也就是域名,端口,协议相同.

比如:我在本地上的域名是127.0.0.1:8000,请求另外一个域名:127.0.0.1:8001一段数据

浏览器上就会报错,个就是同源策略的保护,如果浏览器对javascript没有同源策略的保护,那么一些重要的机密网站将会很危险

已拦截跨源请求:同源策略禁止读取位于 http://127.0.0.1:8001/SendAjax/ 的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-Origin')。

但是注意,项目2中的访问已经发生了,说明是浏览器对非同源请求返回的结果做了拦截

二 CORS(跨域资源共享)简介

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

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

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

三 CORS基本流程

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

四 CORS两种请求详解

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

(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

凡是不同时满足上面两个条件,就属于非简单请求。

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

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

   简单请求:一次请求
   非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输。
* 关于“预检”

- 请求方式:OPTIONS
- “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
- 如何“预检”
     => 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过
        Access-Control-Request-Method
     => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过
        Access-Control-Request-Headers

支持跨域,简单请求

服务器设置响应头:Access-Control-Allow-Origin = ‘域名’ 或 ‘*’

支持跨域,复杂请求

由于复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。

  • “预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method
  • “预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers

五 Django项目中支持CORS

在返回的结果中加入允许信息(简单请求)

def test(request):
    import json
    obj=HttpResponse(json.dumps({'name':'lqz'}))
    # obj['Access-Control-Allow-Origin']='*'
    obj['Access-Control-Allow-Origin']='http://127.0.0.1:8004'
    return obj

放到中间件处理复杂和简单请求:

from django.utils.deprecation import MiddlewareMixin
class CorsMiddleWare(MiddlewareMixin):
    def process_response(self,request,response):
        if request.method=="OPTIONS":
            #可以加*
            response["Access-Control-Allow-Headers"]="Content-Type"
        response["Access-Control-Allow-Origin"] = "http://localhost:8080"
        return response

六 django 使用django-cors-headers 解决跨域问题

1、使用pip安装

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

上面的旧配置不好用可以用新的


# 方案三:
CORS_ALLOW_ALL_ORIGINS = True   # CORS 允许所有来源
# CORS 允许头
CORS_ALLOW_HEADERS = list(default_headers) + [
    'token'
]
CORS_ALLOW_CREDENTIALS = True  # CORS 允许凭据

跨域请求解决

参考1:https://zhuanlan.zhihu.com/p/100783055
参考2:https://www.cnblogs.com/ysocean/p/9380551.html

一篇轻松解决跨域问题

无栈开发

想必不论是前端还是后端的开发同学,在工作和面试中都曾经遇到过接口跨域问题,本篇详细解释跨域及给出解决接口跨域问题的常用方案,希望能帮到曾经对跨域问题仍有疑惑的同学。

什么是跨域

跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域。

如下给出了跨域的几种情况

为什么会有跨域问题

核心原因在于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。

这里有一个关键词浏览器大家一定要记住,受跨域影响的只在浏览器范围。无论是前端和后端一定要记住限制的宿主。因为之前的工作中曾经不止一次的有经验不足的小伙伴问到,为什么接口给app用没问题,pc或者H5一调就不行,肯定是他们调用的问题。这里再提醒一下,native调用接口的时候是没有经过浏览器的。只有通过浏览器发起的ajax请求,在跨域的时候会受到同源限制。app navtive直接调用,或者server端发起的http请求等等均不会受到影响。

受到同源限制的范围

浏览器同源限制的地方主要有如下三处,因为本篇的标题已经明确是接口跨域相关所有后续的方案也只介绍ajax请求相关

  • Cookie、LocalStorage 和 IndexedDB
  • DOM
  • AJAX 请求

相关解决方案

  • 域名归一 解决跨域问题的第一个方案,将跨域的改成不跨域,域名相同了也就不存在跨域了。将前端项目和接口请求,通过反向代理工具如nginx将前端web项目和服务server的域名映射成同一域名即可。该方案适用于没有强制要求新开多个域名的公司内部项目,修改只需要向运维描述需求修改反向代理接口,当然如果没有运维自己调整也很快。
  • JSONP 浏览器中一个域名加载其他域名的script是不受限制的,jsonp的原理是调用的时候动态新增一个script标签扩展到dom上,然后请求脚本资源的形式向服务端发起http请求。server接收到请求后,以约定好的形式用回调函数名包裹数据的方式返回,如jsonpCallbackName({data:data})。script加载完毕后,就会立即执行该回调函数。此时就可以获得该数据。该方案适用于提供给第三方作为http的api,也可用于公司内部。缺点是如果要同时提供给app使用的话,需要接口调整适配app,或者app调整解析方式适配接口。
  • 跨域资源共享 CORS 该方案主要是服务端调整响应头,设置 Access-Control-Allow-Origin。具体设置要看服务端的架构情况。核心就是找到请求的响应出口,设置其response header值。如架构存在反向代理可以在nginx处对应转发处,设置header值。也可以在应用服务器设置header,不论是java的spring系列的跨域注解,go或者nodejs获者其他的web服务器,均记得找到对应设置response header的地方即可。适用公司内部项目,目前比较常用
  • 请求代理 举一个场景,如果你希望直接调用其他第三方的接口,如微博等等其他的第三方的不对外的ajax接口。此时直接在web下面肯定是无法直接通过,可以通过自己的服务器server端发起http请求,然后自己的web去调用自己服务端。适用场景个人项目
  • 其他 还有通过window.name + iframe跨域或者postMessage的方式因其目前不怎么常用,故这里不详细解释,有兴趣的同学可以百度深入了解。
  • 本地调试方案 前后端分离的项目里面,前端调试时跨域还可以通过浏览器(chrome)安装跨域插件,或者在前端项目里面启动代理方便开发核心应该也是基于cors,但是都只能本地调试使用。最终线上环境还是需要依赖以上前面给到的方案解决。

以上即是对当前接口跨域的一些总结分享,希望能够帮到有需要的同学。

解决跨域问题参考3

参考3:https://www.cnblogs.com/x945669/p/14179424.html#_label1

一:跨域请求

跨域问题

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

img

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

img

1.同源策略

简介

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

同源通常指的是 浏览器页面的 协议相同 域名相同 端口相同

同源策略的目的是为了保证用户的信息安全防止恶意的网站窃取信息数据

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

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

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

[img](https://gitee.com/xuexianqi/img/raw/master/img/01 cors01.gif)

非简单请求

原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

④ 访问测试

img

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.测试效果

img

回到顶部

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

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
      }
    }
  }
};

参考:niginx提到跨域

在浏览器上当前访问的网站向另一个网站发送请求获取数据的过程就是跨域请求

跨域是浏览器的同源策略决定的,是一个重要的浏览器安全策略,用于限制一个 origin 的文档或者它加载的脚本与另一个源的资源进行交互,它能够帮助阻隔恶意文档,减少可能被攻击的媒介,可以使用 CORS 配置解除这个限制

posted @ 2021-05-08 15:36  只有时间是永恒  阅读(538)  评论(0)    收藏  举报