后台
插件
>: 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>
<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>