Django + Axios 跨域发送 cookie
为什么会变成这样呢
3 人赞同了该文章
笔者的环境如下:
| 前端 | Vue | localhost:8080 |
| 后端 | Django | http://www.mydomain.com:80 |
可以看到,前后端服务的域名和端口都不一样,因此在前后端进行交互时必然会出现跨域的问题,可以在后端使用 django-cors-headers 处理。
1. django-cors-headers
1.1 配置
pipenv install django-cors-headers # 使用 pipenv 安装
在 Django settings.py 中配置如下
MIDDLEWARE = [
.........
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware', # 注意中间件安装的位置
'django.middleware.common.CommonMiddleware',
.........
]
CORS_ORIGIN_ALLOW_ALL = True # 允许任意站点跨域请求
CORS_ALLOW_CREDENTIALS = True # 允许发送身份验证
理论上应该在 INSTALLED_APPS 里注册 django-cors-headers 的,但笔者没有注册也能正常使用。
1.2 原理
当浏览器发送请求时,Django会在响应头里添加属性 access-control-allow-origin,它的值为客户端的地址(笔者这里为 http://localhost:8080 ),这样一来客户端就会允许跨域请求了。
2. Axios 配置
在 Vue 中对服务器 API 进行访问,通常使用 Axios。在上述的配置下已经能够实现跨域请求,但 Axios 在发送请求时默认是不发送 cookie 的,如果要通过 Axios 传输 cookie 等身份验证信息,还要在 Axios 的中(笔者这里直接在入口文件 main.js 中配置)配置一句
axios.defaults.withCredentials = true;
3. 踩坑记录
但是这时仍然不能正确进行身份验证,在浏览器的返回信息里我们可以看到服务端已经把 cookie 发送给了我们,但是浏览器却拒绝接收。
[图片上传失败...(image-18d7fa-1636527192906)]
这是由于 set-cookie 的 SameSite 属性造成的。从 MDN 文档 中可以了解到:
SameSite 接受下面三个值:
Lax
Cookies允许与顶级导航一起发送,并将与第三方网站发起的GET请求一起发送。这是浏览器中的默认值。(中英文有歧义,以如下英文为准)
Cookies are not sent on normal cross-site subrequests (for example to load images or frames into a third party site), but are sent when a user is*navigating to *the origin site (i.e. when following a link).Strict
Cookies只会在第一方上下文中发送,不会与第三方网站发起的请求一起发送。None
Cookie将在所有上下文中发送,即允许跨域发送。(注意:None 属性还要配合 Secure 属性一起使用)
Chrome 在 56 版本中增加了对于 SameSite 的支持,但那时 SameSite 的默认值被设置为 None,即允许跨域发送 cookie。
然而,在 Chrome 的 80 版本中将 SameSite 的默认值改为了 Lax,这样一来,服务器发送给我们的 cookie 就会被浏览器拦截。
3.2 解决方案
3.2.1 服务端方案
不难想到我们只需要在服务端做一些配置里,对于发送的 cookie 自动添加 SameSite=None 和 Secure 属性,所幸 django-cors-headers 已经提供了方便的配置选项。
在 settings.py 中添加:
SESSION_COOKIE_SAMESITE = 'None'
CSRF_COOKIE_SAMESITE = 'None'
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
需要注意,此处的 None 需要设置为字符串类型,以免与 python 的关键字混淆
3.2.2 客户端方案
考虑到在实际生产项目中我们的前端项目也是部署在相同的域名下的,因此这样的跨域问题只会在开发过程中遇到,这样我们可以直接关闭浏览器的这个新增的功能。
访问 chrome://flags/#same-site-by-default-cookies 并将其设置为 Disabled。
经验总结
留心关注报错提示信息,善用搜索。