通常中小型前后端项目,对安全要求不高,也可以采用密码认证方案。如果只用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/ 接口。否则将拒绝。