本文基于之前的一个旅游网站,实现 Django 连接 vue3,使 vue3 能携带 CSRF Token 发送 axios 请求给后端,后端再响应数据给前端。想要源码直接滑倒底部。
目录
实现效果
Django5连接前端vue3,前后端分离式项目(Django+vue3+csrf-token+axios)
解决跨域
下载解决跨域的包:
pip install django-cors-headers
注册,并配置中间件
INSTALLED_APPS = [
"corsheaders",
]
MIDDLEWARE.insert(0, 'corsheaders.middleware.CorsMiddleware')
允许其他端口来源(仅用于开发环境)
CORS_ALLOW_ALL_ORIGINS = True
获取 csrf-token
什么是 csrf-token ?
Django中的CSRF Token(Cross-Site Request Forgery Token,跨站请求伪造令牌)主要用于防止CSRF攻击。这是一种针对网站的恶意攻击模式,攻击者通过伪装来自受信任用户的请求来利用已认证的用户数据进行非法操作。
CSRF攻击的工作原理
假设你登录了一个银行网站,并且在没有登出的情况下访问了一个恶意网站。如果该银行网站对某些敏感操作(如转账)的安全措施不足,恶意网站可以通过自动提交表单或发送AJAX请求的方式,利用你的身份和已登录状态向银行网站发起转账请求。由于请求是从你的浏览器发出的,同时包含有效的会话Cookie,银行服务器无法区分这个请求是合法的还是伪造的,从而可能导致资金被非法转移。
CSRF Token的作用
为了防止上述情况发生,Django使用CSRF Token作为额外的安全层。具体工作流程如下:
生成Token:当用户访问一个包含表单的页面时,Django会在响应中设置一个名为
csrftoken
的Cookie,并且在HTML表单中插入一个隐藏字段,其值为相同的CSRF Token。验证Token:当用户提交表单时,无论是通过POST请求还是其他非安全方法(如PUT、DELETE等),Django都会检查请求中的CSRF Token是否与存储在Cookie中的Token相匹配。只有当两者匹配时,才会处理该请求;否则,请求将被拒绝并返回403 Forbidden错误。
安全性保障:这种方法有效地阻止了第三方网站直接构造请求并利用已登录用户的会话信息执行未授权操作的可能性,因为它们无法获取到正确的CSRF Token。
在前后端分离项目中的应用
在传统的Django项目中,模板渲染机制使得CSRF Token很容易集成进每个需要保护的表单或AJAX请求中。然而,在前后端分离的应用场景下,前端可能是一个独立运行的Vue.js、React或其他JavaScript框架开发的应用,这种情况下,获取和使用CSRF Token需要一些额外的工作,比如通过特定的API接口获取Token,并确保每次请求都正确地包含了这个Token。这通常涉及到在前端代码中添加逻辑来获取和附加CSRF Token到请求头中。
问题
Django 默认启用了 CSRF 保护机制,要求所有非安全 HTTP 方法(如 POST、PUT、DELETE)必须包含有效的 CSRF Token。如果前端未正确获取或发送 CSRF Token,就会触发以下错误:
CSRF verification failed. Request aborted.
CSRF cookie not set.
在传统的 Django 项目中,CSRF Token 通常是通过模板渲染(如 {% csrf_token %}
)或默认机制生成的,并存储在 Cookie 中,其中,{% csrf_token %}在我之前的所有 Django 教程中都在html页面中编写了
。然而,在前后端分离的架构中:
- 前端和后端是独立运行的。
- 前端可能不会直接加载 Django 提供的页面,因此无法自动获取 CSRF Token。
解决方案
Django 获取 CSRF Token
Django 提供了一个专门的视图 /csrf/
,可以用来手动获取 CSRF Token。你可以通过以下步骤实现:
配置 Django 视图
在 Django 的 urls.py
文件中添加一个视图来返回 CSRF Token:
from django.middleware.csrf import get_token
from django.http import JsonResponse
def get_csrf_token(request):
token = get_token(request)
return JsonResponse({'csrfToken': token})
然后在 urlpatterns
中注册该视图:
from django.urls import path
from . import views
urlpatterns = [
...
path('csrf/', views.get_csrf_token, name='get_csrf_token'),
]
前端获取
配置 Vite 代理
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://127.0.0.1:8080', // 确保与 Django 后端地址一致
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
secure: false, // 如果后端使用 HTTPS,可能需要设置为 true
},
},
},
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
plugins: [vue()],
});
下载 axios
npm install axios
请求 CSRF Token
import axios from 'axios';
let csrfToken = null;
// 获取 CSRF Token
async function fetchCsrfToken() {
try {
const response = await axios.get('/api/csrf/');
csrfToken = response.data.csrfToken;
console.log("CSRF Token:", csrfToken);
} catch (error) {
console.error("Error fetching CSRF token:", error.response?.data || error.message);
}
}
fetchCsrfToken()
运行前后端项目
# 运行后端
python manage.py runserver 8080
// 运行前端
npm run dev
后端处理请求:
前端获取到 token:
有了 token 之后,前端的一系列数据就能被后端安全接收,像用户管理之类的功能就能安全得进行了。
请求与相应
前端如何发送请求给 Django,Django 又如何相应数据给前端?
请求
前面讲的 前端获取 csrf-token 其实就是响应。配置 Vite 代理后,再使用 axios 发送请求给 Django:
这里我再给个示例:
前端通过 /api/ask 发送请求,携带 CSRF Token 请求头,将用户输入的 question 以 json 形式发POST 给 Django :
async function sendQuestion() {
if (!csrfToken) {
console.error("CSRF Token is not available");
return;
}
try {
const response = await axios.post('/api/ask/', { question: question.value }, {
headers: {
'X-CSRFToken': csrfToken, // 添加 CSRF Token 到请求头
'Content-Type': 'application/json', // 指定内容类型为 JSON
},
});
console.log("Response from Django:", response.data);
} catch (error) {
console.error("Error sending question:", error.response?.data || error.message);
}
}
后端定义 url:
path('ask/', views.ai_talk, name='ai_talk'), # 使用类视图
视图函数接收前端的 POST 数据并解析:
def ai_talk(request):
if request.method == 'POST':
try:
# 从请求体中获取 JSON 数据
body = request.body.decode('utf-8') # 将字节流解码为字符串
data = json.loads(body) # 将 JSON 字符串解析为 Python 字典
# 获取用户输入的问题
user_question = data.get('question', '').strip()
print(f"用户输入的问题: {user_question}")
except Exception as e:
# 处理异常
print(f"解析请求数据失败: {e}")
return HttpResponse("请求数据无效", status=400)
else:
# 如果不是 POST 请求,返回错误
return HttpResponse("仅支持 POST 请求", status=405)
得到数据:
响应
后端返回数据
Django 已经接收到了数据,可以通过 HttpResponse 或 JsonResponse 将数据返回,这里使用JsonResponse 以 json格式返回数据,仅需在视图函数中加入返回代码:
# 返回 JSON 响应
return JsonResponse({
'status': 'success',
'message': ai_response,
})
前端接收数据
定义一个列表接收返回数据
const aiResponse = ref<string[]>([]); // 响应数据列表
在前端发送问题的同时,请求后端的响应:
// 发送问题到后端
async function sendQuestion() {
if (!csrfToken) {
console.error("CSRF Token is not available");
return;
}
try {
const response = await axios.post('/api/ask/', { question: question.value }, {
headers: {
'X-CSRFToken': csrfToken, // 添加 CSRF Token 到请求头
'Content-Type': 'application/json', // 指定内容类型为 JSON
},
});
// 获取后端返回的数据
const responseData = response.data;
console.log("Response from Django:", responseData);
if (responseData.status === 'success') {
//添加数据到相应列表
aiResponse.value.push(responseData.message);
} else {
console.error("Error from backend:", responseData.message);
}
// 清空问题输入框
question.value = '';
} catch (error) {
console.error("Error sending question:", error.response?.data || error.message);
}
}
再显示到页面上。
源码获取
上面是 Django 代码,下面是 vue3 代码。
资源地址:https://download.csdn.net/download/2403_83182682/90578132
感谢您的观看!!!