通常中小型前后端项目,对安全要求不高,也可以采用密码认证方案。如果只用django来实现非常简单。采用 Vue3 前后端分离架构,实现起来稍繁琐一点,好处是可以利用各种前端技术栈,如element-plus UI库来渲染页面。
演示项目需求为:
进入保存项目的目录,如d:/workplace/projects/, 运行命令:
npm create vue@latest
这个命令会安装create-vue 工具,并执行创建项目,其过程会显示许多配置选项
新项目的路径为项目名称,即vue02/ , 生成的项目结构如下。
项目默认采用组合式API
D:\workplace\web\vue02>tree /A /F
卷 软件 的文件夹 PATH 列表
卷序列号为 0DC5-179B
D:.
| .gitignore
| index.html
| package.json
| README.md
| vite.config.js
+---.vscode
| extensions.json
+---public
| favicon.ico
\---src
| App.vue
| main.js
|
+---assets
| base.css
| logo.svg
| main.css
|
+---components
| | HelloWorld.vue
| | TheWelcome.vue
| | WelcomeItem.vue
|
+---router
| index.js
|
\---views
AboutView.vue
HomeView.vue
修改App.vue,清空项目。
安装element-plus, axios, pinia
npm install element-plus --save-dev npm install @element-plus/icons-vue --save-dev npm install axios --save-dev npm install pinia --save-dev
在main.js 全局导入依赖库
import { createApp } from 'vue'
import "./assets/main.css"
import App from './App.vue'
import { createPinia } from 'pinia'
import router from './router'
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import formCreate from '@form-create/element-ui'
const app = createApp(App)
app.use(createPinia()) // 导入pinia 库
app.use(router)
app.use(ElementPlus)
app.use(formCreate)
//导入所有elementplus 图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.mount('#app')
创建路由文件 src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
component: () => import('../views/AboutView.vue')
},
{
path: '/order',
name: 'order',
component: () => import("../views/FormOrder.vue")
},
{
path: '/login',
name: 'login',
component: () => import("../views/Login.vue")
},
]
})
export default router
修改 App.vue, 添加布局与导航菜单
Vue3 测试项目 密码登录 JWT登录 演示 显示Blog 新建Blog 订单管理 选项 item one item two Footer
请参考作者另一篇 [博文] (https://blog.csdn.net/captain5339/article/details/131572762) 准备django环境

说明:
Set-Cookie: csrftoken=stUBZaZO26cKbf6RidHmmgiwHAFmY31jFpUbFuMqa8gJycz8WB4DNc6jmNexsqn6; expires=Wed, 19 Mar 2025 10:45:44 GMT; Max-Age=31449600; Path=/; SameSite=Lax Set-Cookie: sessionid=anv6tzhtws4mzdl5hprjcucre1feynyk; expires=Wed, 03 Apr 2024 10:45:44 GMT; HttpOnly; Max-Age=1209600; Path=/; SameSite=Lax
思路:
技术要点:
主要包含
state:
actions:
创建 store 文件: src/stores/userStore.js
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
import axios from 'axios';
export const useUserStore = defineStore('user', () => {
const username = ref('')
const password = ref('')
const loginStatus = ref(false)
const ax = axios.create({
baseURL: 'http://localhost:8000', //请求后端数据的基本地址,自定义
timeout: 2000 //请求超时设置,单位ms
})
ax.defaults.withCredentials = true
const Login = async (userName, pass) => {
try {
const res = await ax({
url: '/v1/api-auth/login/',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
},
data: {
username: userName,
password: pass,
},
})
console.log(res.data)
console.log(res.headers)
if (res.data.result == 'success') {
username.value = userName
loginStatus.value = true
} else {
loginStatus.value = false
}
} catch (error) {
console.log(error)
}
}
//清空state
const clearUserStore = () => {
username.value = ''
password.value = ''
loginStatus.value = false
}
return { username, password, loginStatus, Login, clearUserStore }
})
a) 提供username, password 输入表单
b) 将login表单数据传入 userStore的login()方法。
c) 处理response数据
- 登陆成功:更新loginStatus, 重定向至下一页
- 登陆失败,显示失败信息,继续重试。
组件名称 src/views/Login.vue
登录
a) 修改src/router/router.js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
…
{
path: '/login',
name: 'login',
component: () => import("../views/Login.vue")
},
]
})
export default router
b) 添加菜单项,指向新建路由
src/app.vue
密码登录
注意,django应提供基于api的view ,而非基于页面视图的login view.
from rest_framework import status
from rest_framework.decorators import api_view, authentication_classes
from rest_framework.response import Response
from rest_framework.authentication import (
SessionAuthentication,
BasicAuthentication
)
from django.contrib.auth import authenticate, login, logout
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from .models import *
from .serializers import ArticleSerializer, UserSerializer
@csrf_exempt
def api_login(request):
if request.method == "POST":
print(list(request.POST.items()))
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
# Redirect to a success page.
return JsonResponse({"result": "success"})
else:
# Return an 'invalid login' error message.
return JsonResponse({'result': 'failed' ,'reason': "用户名与密码不正确"})
else:
return JsonResponse({"result": "rejected", "reason": "request method must be post"}, status=403)
@csrf_exempt
def api_logout(request):
logout(request)
return JsonResponse({"result": "success"})
@api_view(['GET','POST'])
@authentication_classes([SessionAuthentication, BasicAuthentication])
def article_list(request, format=None):
"""
List all articles, or create a new article.
"""
if request.method == 'GET':
qs = Article.objects.all()
qs = qs.select_related('author')
serializer = ArticleSerializer(qs, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = ArticleSerializer(data=request.data)
if serializer.is_valid():
# Very important. Associate request.user with author
serializer.save(author=request.user)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@api_view(['GET', 'PUT', 'DELETE'])
def article_detail(request, pk,format=None):
"""
Retrieve,update or delete an article instance。"""
try:
qs = Article.objects.select_related('author')
article = qs.get(pk=pk)
except Article.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = ArticleSerializer(article)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = ArticleSerializer(article, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
article.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
修改app.urls , 添加path
urlpatterns = [
...
path('api-auth/login/', api_login, name='login'),
path('api-auth/logout/',api_logout,name='logout'),
path('articles/', article_list),
...
]
进入django 文件夹,启动server
python manage.py runserver 0.0.0.0:8000
默认服务器端口为 http://127.0.0.1:8000
登录 api url: http://127.0.0.1:8000/v1/api-auth/login/
进入vue3项目文件夹,启动项目
npm run dev
默认前端访问地址:
http://localhost:5173/
通过菜单进入登录表单页,打开浏览器的开发者工具,点击网络选项
输入用户名与密码后,点击提交按钮,axio发送请求至服务器,

服务器端发送响应,vue3组件收到后,弹出登录成功的 message。接口消息可以从开发者工具的网络视图中查看。

后续请求消息处理
如访问 http://127.0.0.1:8000/v1/articles/ 时,可以看到vue3在自动将 sessionid, csrftoken 放进request 的cookie中了。 django服务器根据sessionid 确定该user是否已通过登录验证。如果通过允许访问 /v1/articles/ 接口。否则将拒绝。