Loading

Django跨域问题解决

Django跨域问题解决

今天在学习前端 Vue 框架的过程中,遇到了 跨域 相关问题

一、为什么会出现跨域

出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)

二、什么是跨域

当前页面URL 被请求的URL 是否跨域 原因
http://www.baidu.com http://www.baidu.com/index.html 同源(协议、域名、端口号相同)
http://www.baidu.com https://www.baidu.com/index.html 跨域 协议不同(http/https)
http://www.baidu.com http://www.taobao.com/ 跨域 主域名不同(baidu/taobao)
http://www.baidu.com http://ditu.baidu.com/ 跨域 子域名不同(www/ditu)
http://www.baidu.com http://www.baidu.com:8888/ 跨域 端口号不同(80/8888)

三、简单请求和复杂请求

当我们用到跨域资源共享(简称cors)来解决跨域问题,但是在使用cors的时候,http请求会被划分为两类,简单请求和复杂请求,而这两种请求的区别主要在于在跨域中是否会触发预检请求

以下的 HTTP headers都可以被认为是简单头部:

  • Accept,
  • Accept-Language,
  • Content-Language,
  • Content-Type并且值是 application/x-www-form-urlencoded, multipart/form-data, 或者 text/plain之一的(忽略参数).

当只包含简单头部时,一个请求则被视为简单请求并且在CORS中不需要发送预检请求。

简单来说,简单请求时直接发送请求,而复杂请求(有其他的头部请求或者Content-Type不是以上三种)会发送一个 OPTIONS 预检请求。当预检请求通过时,才会发起真正的请求,所以通常复杂请求通常需要两次请求才能得到结果。

MDN参考文章: 简单头部
MDN参考文章: 预检请求

四、遇到的问题

问题1详情:

Access to XMLHttpRequest at 'http://127.0.0.1:8000/book/' from origin 'http://localhost:63342' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource

问题2详情:
通过百度,我视图的返回中配置了 response['Access-Control-Allow-Origin'] = '*'
但是我们已经配置过跨域 ,而且 GET 请求已经成功,但是当我发送一个 DELETE 请求时,却依然报错。

Access to XMLHttpRequest at 'http://127.0.0.1:8000/book/5/' from origin 'http://localhost:63342' has been blocked by CORS policy: Method DELETE is not allowed by Access-Control-Allow-Methods in preflight response.

同时我们发送的明明是 DELETE 请求, 而我们后端接收到的却是一个 OPTIONS 请求。

后面发现这是上文提到的 简单请求和复杂请求 导致的。

五、解决跨域

3.1 自定义中间件

由于每个响应头都需要设置,我们可以直接将功能放入自定义中间件。

自定义中间件 mymiddleware.py

from django.utils.deprecation import MiddlewareMixin


class CrossDomainMiddle(MiddlewareMixin):
    def process_response(self, request, response):
        # 简单请求
        response['Access-Control-Allow-Origin'] = '*'  # 允许所有客户端
        # 非简单请求
        if request.method == 'OPTIONS':
            response['Access-Control-Allow-Methods'] = '*'
            response['Access-Control-Allow-Headers'] = '*'
        return response

注册中间件 settings.py

MIDDLEWARE = [
    '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',
    'app01.mymiddleware.CrossDomainMiddle' # 注册自定义的中间件
]

3.3 存在问题

  • 解决方法

明确的设置响应头 Access-Control-Allow-Methods 中的跨域允许的请求方法。这个字段在简单请求的的时候比如 GET/POST 用 * 或者 不写 都可以,但是当使用 PUT/DELETE 等请求的时候,必须明确规定写出来

我们可以在 视图函数 中设置响应头或者通过 自定义中间件 (推荐) 统一设置

response['Access-Control-Allow-Origin'] = '*' # 设置 跨域允许的 请求源
response['Access-Control-Allow-Methods'] = 'GET,POST,OPTIONS,PUT,DELETE' # 设置 跨域允许的 请求方法

3.4 使用 django-cors-headers 模块

当然,像跨域这样的问题,早就有第三方模块帮我们解决好了,而且更为完美。我们可以不用手动处理,只需要使用别人的模块即可。

  • 安装模块
pip install django-cors-headers
  • 在 settings.py 中注册应用
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config', # 你自己的应用
    'rest_framework',
    'corsheaders' # 注册 corsheaders
]
  • 在 settings.py 中注册中间件
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware', # 必须放在这个位置 CommonMiddleware 的上方
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware', # 可以注释此中间件取消 csrf 认证
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
  • 其他部分配置

在 settings.py 下方添加即可

############# 配置允许访问的域名白名单 #############
# 允许所有 域名/IP 跨域
CORS_ALLOW_ALL_ORIGINS = True
# 配置可跨域访问的 域名/IP
CORS_ALLOWED_ORIGINS = [
    '127.0.0.1:8000',
    'localhost:8080',
    'myname.com',
]
# 使用正则表达式匹配允许访问的 域名/IP
CORS_ALLOWED_ORIGIN_REGEXES = [
    r"^https://\w+.example.com$",
]
############# 配置允许的跨域请求方式 #############
# 配置允许的请求方式
CORS_ALLOW_METHODS = [
    '*', # * 表示允许全部请求头
    'GET',
    'POST',
    'PUT',
    'PATCH',
    'DELETE',
    'OPTIONS'
]

############# 配置允许的请求头 #############
CORS_ALLOW_HEADERS = [
    "accept",
    "accept-encoding",
    "authorization",
    "content-type",
    "dnt",
    "origin",
    "user-agent",
    "x-csrftoken",
    "x-requested-with",
]

############# 允许跨域访问Cookie #############
# 改为True即为可跨域设置Cookie
CORS_ALLOW_CREDENTIALS = True
​
# 这里有一个需要注意的点
# chrome升级到80版本之后,cookie的SameSite属性默认值由None变为Lax
# 也就是说允许同站点跨域 不同站点需要修改配置为 None(需要将Secure设置为True)
# 需要前端与后端部署在统一服务器下才可进行跨域cookie设置
​
# 总结:需要设置 samesite = none、secure = True(代表安全环境 需要 localhost 或 HTTPS)才可跨站点设置cookie

以上便是我遇见的两个与跨域相关的问题。

参考博客 https://blog.csdn.net/weixin_43939159/article/details/109676307
参考博客 https://juejin.cn/post/7171036423674396685

posted @ 2023-09-22 19:15  Roy-Huang  阅读(907)  评论(0)    收藏  举报