多方式登录

后台

插件
>: pip install djangorestframework-jwt
urls.py
path('login/', views.LoginViewSet.as_view({'post': 'login'})),
dev.py
import datetime
JWT_AUTH = {'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
}
views.py
from rest_framework.viewsets import ViewSet
from . import serializers, models
from utils.response import APIResponse
class LoginViewSet(ViewSet):# 局部禁用认证、权限组件authentication_classes = ()permission_classes = ()def login(self, request, *args, **kwargs):serializer = serializers.LoginSerializer(data=request.data, context={'request': request})if serializer.is_valid():token = serializer.context.get('token')# 原来要一个个拿信息# username = serializer.context.get('username')# icon = serializer.context.get('icon')# 拿到登录用户,直接走序列化过程,将要返回给前台的数据直接序列化好给前台user = serializer.context.get('user')result = serializers.LoginSerializer(user, context={'request': request}).dataresult['token'] = token  # id,username,icon,tokenreturn APIResponse(result=result)return APIResponse(1, serializer.errors)
serializers.py
from rest_framework import serializers
from rest_framework import exceptions
from django.conf import settings
from . import modelsclass LoginSerializer(serializers.ModelSerializer):# 覆盖,避免login校验username有数据库唯一字段约束的限制username = serializers.CharField()class Meta:model = models.User# username、password可以通过局部钩子指定详细的校验规则fields = ('id', 'username', 'password', 'icon')extra_kwargs = {'id': {'read_only': True,},'icon': {'read_only': True,},'password': {'write_only': True,}}def validate(self, attrs):# 多方式得到useruser = self._get_user(attrs)# user签发tokentoken = self._get_token(user)# token用context属性携带给视图类self.context['token'] = token''' 自己将user的信息逐个处理,传给视图# 前台可能不仅仅只需要登录成功的token,可能还需要用户名、用户头像等self.context['username'] = user.username# 通过请求头格式化iconrequest = self.context['request']icon = 'http://%s%s%s' % (request.META['HTTP_HOST'], settings.MEDIA_URL, user.icon)self.context['icon'] = icon'''# 将登录用户对象直接传给视图self.context['user'] = userreturn attrsdef _get_user(self, attrs):import reusername = attrs.get('username')if re.match(r'^1[3-9][0-9]{9}$', username):user = models.User.objects.filter(mobile=username).first()else:user = models.User.objects.filter(username=username).first()if not user:raise exceptions.ValidationError({'username': 'username error'})password = attrs.get('password')if not user.check_password(password):raise exceptions.ValidationError({'password': 'password error'})return userdef _get_token(self, user):from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handlerpayload = jwt_payload_handler(user)token = jwt_encode_handler(payload)return token

前台

Login.vue
<template><div class="login"><div class="box"><i class="el-icon-close" @click="close_login"></i><div class="content"><div class="nav"><span :class="{active: login_method === 'is_pwd'}"@click="change_login_method('is_pwd')">密码登录</span><span :class="{active: login_method === 'is_sms'}"@click="change_login_method('is_sms')">短信登录</span></div><el-form v-if="login_method === 'is_pwd'"><el-inputplaceholder="用户名/手机号/邮箱"prefix-icon="el-icon-user"v-model="username"clearable></el-input><el-inputplaceholder="密码"prefix-icon="el-icon-key"v-model="password"clearableshow-password></el-input><el-button type="primary" @click="login">登录</el-button></el-form><el-form v-if="login_method === 'is_sms'"><el-inputplaceholder="手机号"prefix-icon="el-icon-phone-outline"v-model="mobile"clearable@blur="check_mobile"></el-input><el-inputplaceholder="验证码"prefix-icon="el-icon-chat-line-round"v-model="sms"clearable><template slot="append"><span class="sms" @click="send_sms">{{ sms_interval }}</span></template></el-input><el-button type="primary">登录</el-button></el-form><div class="foot"><span @click="go_register">立即注册</span></div></div></div></div>
</template><script>export default {name: "Login",data() {return {username: '',password: '',mobile: '',sms: '',login_method: 'is_pwd',sms_interval: '获取验证码',is_send: false,}},methods: {close_login() {this.$emit('close')},go_register() {this.$emit('go')},change_login_method(method) {this.login_method = method;},check_mobile() {if (!this.mobile) return;if (!this.mobile.match(/^1[3-9][0-9]{9}$/)) {this.$message({message: '手机号有误',type: 'warning',duration: 1000,onClose: () => {this.mobile = '';}});return false;}this.is_send = true;},send_sms() {if (!this.is_send) return;this.is_send = false;let sms_interval_time = 60;this.sms_interval = "发送中...";let timer = setInterval(() => {if (sms_interval_time <= 1) {clearInterval(timer);this.sms_interval = "获取验证码";this.is_send = true; // 重新回复点击发送功能的条件} else {sms_interval_time -= 1;this.sms_interval = `${sms_interval_time}秒后再发`;}}, 1000);},login() {if (!(this.username && this.password)) {this.$message({message: '请填好账号密码',type: 'warning',duration: 1500});return false  // 直接结束逻辑}this.$axios({url: this.$settings.base_url + '/user/login/',method: 'post',data: {username: this.username,password: this.password,}}).then(response => {let username = response.data.result.username;let token = response.data.result.token;let user_id = response.data.result.id;this.$cookies.set('username', username, '7d');this.$cookies.set('token', token, '7d');this.$cookies.set('user_id', user_id, '7d');this.$emit('success', response.data.result);}).catch(error => {}).catch(error => {console.log(error.response.data)})}}}
</script><style scoped>.login {width: 100vw;height: 100vh;position: fixed;top: 0;left: 0;z-index: 10;background-color: rgba(0, 0, 0, 0.3);}.box {width: 400px;height: 420px;background-color: white;border-radius: 10px;position: relative;top: calc(50vh - 210px);left: calc(50vw - 200px);}.el-icon-close {position: absolute;font-weight: bold;font-size: 20px;top: 10px;right: 10px;cursor: pointer;}.el-icon-close:hover {color: darkred;}.content {position: absolute;top: 40px;width: 280px;left: 60px;}.nav {font-size: 20px;height: 38px;border-bottom: 2px solid darkgrey;}.nav > span {margin: 0 20px 0 35px;color: darkgrey;user-select: none;cursor: pointer;padding-bottom: 10px;border-bottom: 2px solid darkgrey;}.nav > span.active {color: black;border-bottom: 3px solid black;padding-bottom: 9px;}.el-input, .el-button {margin-top: 40px;}.el-button {width: 100%;font-size: 18px;}.foot > span {float: right;margin-top: 20px;color: orange;cursor: pointer;}.sms {color: orange;cursor: pointer;display: inline-block;width: 70px;text-align: center;user-select: none;}
</style>
Header.vue
<template><div class="header"><div class="slogan"><p>老男孩IT教育 | 帮助有志向的年轻人通过努力学习获得体面的工作和生活</p></div><div class="nav"><ul class="left-part"><li class="logo"><router-link to="/"><img src="../assets/img/head-logo.svg" alt=""></router-link></li><li class="ele"><span @click="goPage('/free-course')" :class="{active: url_path === '/free-course'}">免费课</span></li><li class="ele"><span @click="goPage('/actual-course')" :class="{active: url_path === '/actual-course'}">实战课</span></li><li class="ele"><span @click="goPage('/light-course')" :class="{active: url_path === '/light-course'}">轻课</span></li></ul><div class="right-part"><div v-if="!token"><span @click="put_login">登录</span><span class="line">|</span><span @click="put_register">注册</span></div><div v-else><span>{{ username }}</span><span class="line">|</span><span @click="logout">注销</span></div></div></div><Login v-if="is_login" @close="close_login" @go="put_register" @success="success_login" /><Register v-if="is_register" @close="close_register" @go="put_login" /></div>
</template><script>import Login from './Login'import Register from "./Register"export default {name: "Header",data() {return {url_path: sessionStorage.url_path || '/',token: '',username: '',user_id: '',is_login: false,is_register: false,}},methods: {goPage(url_path) {// 已经是当前路由就没有必要重新跳转if (this.url_path !== url_path) {this.$router.push(url_path);}sessionStorage.url_path = url_path;},put_login() {this.is_login = true;this.is_register = false;},put_register() {this.is_login = false;this.is_register = true;},close_login() {this.is_login = false;},close_register() {this.is_register = false;},success_login(data) {this.is_login = false;this.username = data.username;this.token = data.token;this.user_id = data.user_id;},logout() {this.token = '';this.username = '';this.user_id = '';this.$cookies.remove('username');this.$cookies.remove('token');this.$cookies.remove('user_id');}},created() {sessionStorage.url_path = this.$route.path;this.url_path = this.$route.path;// 检测cookies,查看登录状态this.username = this.$cookies.get('username');this.token = this.$cookies.get('token');this.user_id = this.$cookies.get('user_id');},components: {Login,Register,}}
</script><style scoped>.header {background-color: white;box-shadow: 0 0 5px 0 #aaa;}.header:after {content: "";display: block;clear: both;}.slogan {background-color: #eee;height: 40px;}.slogan p {width: 1200px;margin: 0 auto;color: #aaa;font-size: 13px;line-height: 40px;}.nav {background-color: white;user-select: none;width: 1200px;margin: 0 auto;}.nav ul {padding: 15px 0;float: left;}.nav ul:after {clear: both;content: '';display: block;}.nav ul li {float: left;}.logo {margin-right: 20px;}.ele {margin: 0 20px;}.ele span {display: block;font: 15px/36px '微软雅黑';border-bottom: 2px solid transparent;cursor: pointer;}.ele span:hover {border-bottom-color: orange;}.ele span.active {color: orange;border-bottom-color: orange;}.right-part {float: right;}.right-part .line {margin: 0 10px;}.right-part span {line-height: 68px;cursor: pointer;}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/647174.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

redis持久化之RDBAOF压缩

前引 1、redis持久化的文件是什么 dump.rdb appendonly.aof 2、这两中文件有什么异同 save 秒 1 alaways everysec no 3、文件存放的位置 dir ./ 4、默认的存放位置:命令启动的地方 dir 自定义的路径 rdb 和aof 文件 存放在同一个路径下面 5、rdb文件默认备份的策略是什么&…

redis + 拦截器 :防止数据重复提交

1.项目用到,不是核心 我们干系统开发,不免要考虑一个点&#xff0c;数据的重复提交。 我想我们之前如果要校验数据重复提交要求&#xff0c;会怎么干?会在业务层&#xff0c;对数据库操作&#xff0c;查询数据是否存在,存在就禁止插入数据; 但是吧,我们每次crud操作都会连接…

【docker】解决docker overlay2目录占用大量磁盘空间,导致验证码出不来,报错Can‘t create output stream!

问题&#xff1a; 验证码出现Cant create output stream!报错信息 排查&#xff1a; 所在服务器磁盘使用率已经到达100%&#xff0c;经排查&#xff0c;服务器目录/var/lib/docker/overlay2占用大量磁盘空间&#xff0c; 解决&#xff1a; 使用【docker system prune】命令删…

基于移动边缘计算 (MEC) 的资源调度分配优化研究(提供MATLAB代码)

一、优化模型简介 边缘计算资源调度优化模型是为了解决边缘计算场景下的资源分配和任务调度问题而提出的一种数学模型。该模型旨在通过优化算法来实现资源的有效利用和任务的高效执行&#xff0c;以提高边缘计算系统的性能和用户的服务体验。 在边缘计算资源调度优化模型中&a…

Python基础学习 -04

编码 默认情况下&#xff0c;Python 3 源码文件以 UTF-8 编码&#xff0c;所有字符串都是 unicode 字符串。 当然你也可以为源码文件指定不同的编码&#xff1a; # -*- coding: cp-1252 -*- 上述定义允许在源文件中使用 Windows-1252 字符集中的字符编码&#xff0c;对应适合…

Dapper中使用Tuple元组报错问题

在一次dapper查询中为了不返回多余的字段,也为了方便创建自定义类,使用了元组Tuple<guid,sytring>,查询如下 /// <summary>/// 获取企业集合/// </summary>/// <param name="userId"></param>/// <returns></returns>p…

LeetCode_13_简单_罗马数字转整数

文章目录 1. 题目2. 思路及代码实现&#xff08;Python&#xff09;2.1 模拟 1. 题目 罗马数字包含以下七种字符: I I I&#xff0c; V V V&#xff0c; X X X&#xff0c; L L L&#xff0c; C C C&#xff0c; D D D 和 M M M 。 字符数值 I I I1 V V V5 X X X10 L L …

【python题解17】给你一个有符号整数x,返回将x中的数字部分反转后的结果。输入的整数不超过int类型的最大范围。

1. 题目&#xff1a;给你一个有符号整数x&#xff0c;返回将x中的数字部分反转后的结果。输入的整数不超过int类型的最大范围。 输入样例&#xff1a;-123 输出样例&#xff1a;-321 2. 源代码 n int(input()) flag True #代表正数 if n < 0: #当n是负数时候&#xff…

美易平台:诺基亚四季度财报超预期

正文&#xff1a; 近日&#xff0c;诺基亚发布了其四季度财报&#xff0c;显示调整后营业利润达到了8.46亿欧元&#xff0c;超出市场预估的7.627亿欧元。同时&#xff0c;调整后每股收益&#xff08;EPS&#xff09;为0.10欧元&#xff0c;符合市场预期。这一成绩表明诺基亚在…

前端学习:HTTP协议、请求响应、分层解耦

HTTP协议 HTTP-概述 HTTP&#xff1a;Hyper Text Transfer Protocol(超文本传输协议)&#xff0c;规定了浏览器与服务器之间数据传输的规则。如果想知道http协议的数据传输格式有哪些&#xff0c;可以打开浏览器&#xff0c;点击 F12 打开开发者工具&#xff0c;点击Network 来…

vue router 右到左过渡动画

Vue Router提供了内置的路由切换效果&#xff0c;可以通过设置<transition>组件来添加过渡动画。要实现从右向左的过渡动画&#xff0c;需要使用CSS样式来定义过渡效果。 首先&#xff0c;在Vue项目中安装并引入Vue Router库&#xff1a; npm install vue-router --sav…

Java零基础学习21:学生管理系统

编写博客目的&#xff1a;本系列博客均根据B站黑马程序员系列视频学习和编写目的在于记录自己的学习点滴&#xff0c;方便后续回忆和查找相关知识点&#xff0c;不足之处恳请各位有缘的朋友指正。 一、管理系统初级版 黑马教授的管理系统初级版本 package StudentSystem;imp…

洛谷刷题-【入门3】循环结构

1.找最小值 题目描述 给出 n 和 n 个整数 ai​&#xff0c;求这 n 个整数中最小值是什么。 输入格式 第一行输入一个正整数 n&#xff0c;表示数字个数。 第二行输入 n 个非负整数&#xff0c;表示 a1,a2…an&#xff0c;以空格隔开。 输出格式 输出一个非负整数&#xff0c;表…

致远OA如何开发 第十一篇 能做什么需求

能做什么需求 此栏目技术支持 技术大佬对栏目文章的支持 特别感谢 前言 前面我们讲过如何开发的一些基础&#xff0c;但是没有对应的思维&#xff0c;比如这些开发技巧能够做什么&#xff0c;所以即便是知道需求&#xff0c;也不知道如何实现 例子 1&#xff0c;前端页面…

React16源码: React中commit阶段的commitBeforeMutationLifecycles的源码实现

commitBeforeMutationLifecycles 1 &#xff09;概述 在 react commit阶段的 commitRoot 第一个while循环中调用了 commitBeforeMutationLifeCycles现在来看下&#xff0c;里面发生了什么 2 &#xff09;源码 回到 commit 阶段的第一个循环中&#xff0c;在 commitRoot 函数…

命名强制类型转换

命名强制类型转换 1. static_cast&#xff1a; static_cast 主要用于编译时的类型转换&#xff0c;它通常用于类层次结构中的相关类型、基本数据类型之间的转换以及一些隐式类型转换的显式表示。 任何具有明确的类型转换&#xff0c;只要不包含底层const&#xff0c;都可以使…

E7数据库备份和恢复

E7数据库备份和恢复 一、实验目的 在Mysql上&#xff0c;学习如何备份数据库和恢复的各种方法。 二、实验要求: 1、基本硬件配置:英特尔Pentium III 以上,大于4G内存&#xff1b; 2、软件要求:Mysql&#xff1b; 3、时间:1小时&#xff1b; 4、撰写实验报告并按时提交。 三、…

C++刷题日记:Day 1

题目描述 小明是一野生动物园的管理人员&#xff0c;他统计了一份野生动物的名单&#xff0c;糟糕的是&#xff0c;因为操作不当导致打乱了名单&#xff0c;每种野生动物出现的次数都无法进行查询。 小明只能重新进行统计&#xff0c;已知名单中的动物名称只由大小写字母构成&a…

实用工具合集(持续更新...)

一、搜索引擎 1.1、小白盘 网站&#xff1a;https://www.xiaobaipan.com 度盘资源搜索的网站&#xff0c;能够搜索电影、电视剧、小说、音乐等资源&#xff08;注意&#xff1a;评论区很多小伙伴说小白盘有毒&#xff0c;我用谷歌浏览器搜索过几次并无大碍&#xff0c;请慎用…

如何荒废你的2024?

这个演讲题目出人意料&#xff0c;当大家都在想着如何才能获得幸福、快乐、成功的时候&#xff0c;芒格却在跟我们讲“如何获得痛苦&#xff1f;” 为什么&#xff1f; 因为只有知道了自己不想过怎样的生活&#xff0c;才能想尽办法避免它&#xff1b;同样&#xff0c;也只有…