使用Python和Vue实现用户的登录注册

目录

实现登录注册的准备分析:

django

  3.注册 

  4.登录 

vue 

  1.注册视图(element-ui)

  2.登录视图 (element-ui)


这里以Python中的django框架为例(简略版)

实现登录注册的准备分析:

  • 用户模块
  1. 模型类:继承AbstractUser
  2. 注册:验证用户信息(验证手机号和用户名的重复性,图形验证码)
  3. 登录:验证用户名、密码是否正确,生成token

django

1.创建User模型类

from django.db import models
from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    mobile = models.CharField('手机号',max_length=11)

    def __str__(self):
        return self.username

    class Meta:
        db_table = 'user_tb'
        verbose_name = '用户表'
        verbose_name_plural = verbose_name

修改setting中的配置项

AUTH_USER_MODEL = 'users.User'

2.生成图片验证码 

生成图片验证码流程图

import string
import random
import redis
from captcha.image import ImageCaptcha
from django.http import HttpResponse



class GenImageCode(APIView):
    def get(self,request,uuid):
        #  1.生成随机字符串

        alpha_number = string.ascii_letters + string.digits
        img_code = "".join(random.sample(alpha_number,4))
        # 2.生成图片验证码
        img_captcha = ImageCaptcha()
        img_code_capucha = img_captcha.generate(img_code)

        # 3. 存储图片验证码
        redis_conn = redis.Redis(host='localhost',port=6379,db=0)
        key = 'img_code:%s'%uuid
        redis_conn.set(key,img_code,ex=300)
        redis_conn.close()
        return HttpResponse(img_code_capucha,content_type='image/png')

3.注册 

  • 获取参数
  • 校验参数
  • 存入数据库
  • 返回响应
import re
import redis
import string
import random

from users.models import *
from django.http import HttpResponse
from captcha.image import ImageCaptcha
from rest_framework.views import APIView
from rest_framework.response import Response


class CheckUsername(APIView):
    def get(self,request,username):
        # 1.查询当前用户名是否存在
        if not re.findall(r'^[a-zA-Z][a-zA-Z0-9_]{4,15}$',username):
            return Response({'code':204,'msg':'用户名格式不正确'})
        try:
            User.objects.get(username=username)
        except Exception as e:
            return Response({'code':200,'msg':'用户名可以使用'})

        return Response({'code':204,'msg':'用户已存在'})


# 校验手机号的重复性
class CheckMobile(APIView):
    def get(self,request,mobile):
        if not re.findall(r'^1[3-9]\d{9}$',mobile):
            return Response({'code':204,'msg':'手机号格式不合法'})
        user_query_set = User.objects.filter(mobile=mobile)
        if not user_query_set:
            return Response({'code':200,'msg':'可以正常注册'})

        return Response({'code':204,'msg':'手机号已注册'})


#  图片验证码
class GenImageCode(APIView):
    def get(self,request,uuid):
        #  1.生成随机字符串

        alpha_number = string.ascii_letters + string.digits
        img_code = "".join(random.sample(alpha_number,4))
        # 2.生成图片验证码
        img_captcha = ImageCaptcha()
        img_code_capucha = img_captcha.generate(img_code)

        # 3. 存储图片验证码
        redis_conn = redis.Redis(host='localhost',port=6379,db=0)
        key = 'img_code:%s'%uuid
        redis_conn.set(key,img_code,ex=300)
        redis_conn.close()
        return HttpResponse(img_code_capucha,content_type='image/png')

class CheckImageCode(APIView):
    def get(self,request):
        # 1.获取前端数据
        uuid_ = request.query_params.get('imageCodeID')
        image_code = request.query_params.get('imageCode')

        # 2.根据uuid构造key
        key = "img_code:%s"%uuid_

        # 3.读取redis存储的验证码
        redis_conn = redis.Redis(host='localhost',port=6379,db=0)
        stored_image_code = redis_conn.get(key)
        if not stored_image_code:
            return Response({'code':204,"msg":'图片验证码已过期'})
        if stored_image_code.decode().lower() == image_code.lower():
            return Response({'code':200,'msg':'图片验证成功'})
        return Response({'code':204,"msg":'验证码输入错误'})
# 用户注册
class Register(APIView):
    def post(self,request):
        # 1.获取前端数据
        username = request.data.get('userName')
        password = request.data.get('pwd')
        # confirm_password = request.data.get('pass')
        mobile = request.data.get('mobile')
        aggrement = request.data.get('agree')

        # 2.简单验证
        if not all([username,password,mobile,aggrement]):
            return Response({'code':204,'msg':'用户信息不完整'})

        # if password != confirm_password:
        #     return Response({'code':204,'msg':'两次密码不一致'})

        # 3.创建用户对象,并存储
        User.objects.create_user(username=username,password=password,mobile=mobile)
        return Response({'code':200,'msg':'注册成功'})

4.登录 

  • 获取参数
  • 校验参数
  • 生成token
  • 返回响应
import jwt
import time

from users.models import *

from django.db import models
from rest_framework.response import Response
from django.contrib.auth.hashers import check_password



# 用户登录
class Login(APIView):
    def post(self,request):
        # 获取前端数据
        username = request.data.get('user')
        password = request.data.get('pwd')

        # 2.查询用户
        try:
            user = User.objects.get(models.Q(username=username) | 
                models.Q(mobile=username))
        except:
            return Response({'code':204,"msg":'用户不存在'})

        # 3.验证密码
        validate = check_password(password,user.password)
        if validate:
            # 验证通过,允许登录
            request.session['username'] = username
            token = self.gen_token(user)
            return Response({'code':200,'msg':'欢迎%s'%username,'user':      
               {'userName':user.username},'token':token})
        return Response({'code':204,'msg':'用户名或密码错误'})

    @staticmethod
    def gen_token(user):

        # 第一种方案
        # 组织payload数据
        payload = {
            'username':user.username,
            'exp':time.time()+30000
        }

        # 使用jwt编码,生成token
        token = jwt.encode(payload=payload,key=settings.SECRET_KEY,algorithm='HS256')

        return token

vue 

1.注册视图(element-ui)

  • 使用v-model绑定表单,获取用户信息
  • 点击注册按钮,执行注册方法
  • axios的post请求提交参数

  • 接收响应

<div id="register">
    <el-dialog title="注册" width="300px" center :visible.sync="isRegister">
      <el-form
        :model="RegisterUser"
        :rules="rules"
        status-icon
        ref="ruleForm"
        class="demo-ruleForm"
      >
        <!-- 用户名 -->
        <el-form-item prop="name">
          <el-input
            prefix-icon="el-icon-user-solid"
            placeholder="请输入账号"
            v-model="RegisterUser.name"
          ></el-input>
        </el-form-item>
        <!-- 密码 -->
        <el-form-item prop="pass">
          <el-input
            prefix-icon="el-icon-view"
            type="password"
            placeholder="请输入密码"
            v-model="RegisterUser.pass"
          ></el-input>
        </el-form-item>
        <!-- 确认密码 -->
        <el-form-item prop="confirmPass">
          <el-input
            prefix-icon="el-icon-view"
            type="password"
            placeholder="请再次输入密码"
            v-model="RegisterUser.confirmPass"
          ></el-input>
        </el-form-item>
        
        <!-- 输入手机号 -->
        <el-form-item prop="mobile">
          <el-input
            prefix-icon="el-icon-user-solid"
            placeholder="请输入手机号"
            v-model="RegisterUser.mobile"
          ></el-input>
        </el-form-item>

        <!-- 增加的内容 -->
        <el-form-item prop='imageCode'>
          <!-- 图片验证码 -->
          <el-input 
          placeholder="输入验证码" 
          v-model="RegisterUser.imageCode"
          :style="{width:'60%'}"
          ></el-input>
          <img class='imageCode' v-bind:src="iamgeCodeUrl" alt="图形验证码"         
             @click="genImageCode">
        </el-form-item>

        <!-- 点击注册 -->
        <el-form-item>
          <el-button
            size="medium"
            type="primary"
            @click="Register"
            style="width: 100%"
            >注册</el-button
          >
        </el-form-item>
      </el-form>
    </el-dialog>
  </div>



<script>
// 生成uuid
import {v4 as uuid4} from 'uuid'
export default {
  name: "MyRegister",
  props: ["register"],
  data() {
    // 用户名的校验方法
    let validateName = (rule, value, callback) => {
      if (!value) {
        return callback(new Error("请输入用户名"));
      }
      // 用户名以字母开头,长度在5-16之间,允许字母数字下划线
      const userNameRule = /^[a-zA-Z][a-zA-Z0-9_]{4,15}$/;
      if (userNameRule.test(value)) {
        // 前端校验,用户名复合规则
        //请求后端, 判断用户名是否重复
        this.$axios
          .get("/users/check/username/" + this.RegisterUser.name + "/"
          )
          .then((res) => {
            // 200代表用户名不重复,可以注册
            console.log("校验用户名是否重复:", res);
            if (res.data.code == 200) {
              this.$refs.ruleForm.validateField("checkPass");
              return callback();
            } else {
              // 用户名重复或者不符合规则
              return callback(new Error(res.data.msg));
            }
          })
          .catch((err) => {
            return Promise.reject(err);
          });
      } else {
        // 前端校验,用户名不符合规则
        return callback(new Error("字母开头,长度5-16之间,允许字母数字下划线"));
      }
    };
    // 手机号的校验方法
    let validateMobile = (rule, value, callback) => {
      if (value === "") {
        return callback(new Error("请输入手机号"));
      }
      // 手机号以1开头,第二位3-9之间的数字,长度为11,只允许数字
      const mobileRule = /^1[3-9]\d{9}$/;
      if (mobileRule.test(value)) {
        this.$axios.get("/users/check/mobile/" + this.RegisterUser.mobile + "/")
        .then(res=>{
          console.log("验证手机号是否可用:", res)
          if(res.data.code == 200){
            this.$refs.ruleForm.validateField("checkPass");
            return callback();
          }else{
            return callback(new Error(res.data.msg))
          }
        }).catch(err=>{
          return Promise.reject(err)
        })
      } else {
        return callback(new Error("手机号不符合格式"));
      }
    };
    // 密码的校验方法
    let validatePass = (rule, value, callback) => {
      if (value === "") {
        return callback(new Error("请输入密码"));
      }
      // 密码以字母开头,长度在6-18之间,允许字母数字和下划线
      const passwordRule = /^[a-zA-Z]\w{5,17}$/;
      if (passwordRule.test(value)) {
        this.$refs.ruleForm.validateField("checkPass");
        return callback();
      } else {
        return callback(
          new Error("字母开头,长度6-18之间,允许字母数字和下划线")
        );
      }
    };
    // 确认密码的校验方法
    let validateConfirmPass = (rule, value, callback) => {
      if (value === "") {
        return callback(new Error("请输入确认密码"));
      }
      // 校验是否以密码一致
      if (this.RegisterUser.pass != "" && value === this.RegisterUser.pass) {
        this.$refs.ruleForm.validateField("checkPass");
        return callback();
      } else {
        return callback(new Error("两次输入的密码不一致"));
      }
    };
    // 校验图片验证码
    let validateImageCode = (rule, value, callback) => {
      if (value === "") {
        return callback(new Error("请输入图片验证码"));
      }
      // 图片验证码是由字母、数字组成,长度为4
      const iamgeCodeRule = /^[a-zA-Z0-9]{4}$/;
      if (iamgeCodeRule.test(value)) {
        this.$axios.get("/users/check_image_code/", {
          params:{
            imageCodeID: this.imageCodeID,
            imageCode: this.RegisterUser.imageCode,
          }
        })
        .then(res => {
          if(res.data.code == 200){
            this.$refs.ruleForm.validateField("checkPass");
            return callback();
          }else{
            return callback(new Error(res.data.msg))
          }
        }).catch(err => {
          return Promise.reject(err)
        })
      } else {
        return callback(new Error("图片验证码不正确!"));
      }
    };
    return {
      imageCodeID: "",//即生成的uuid
      iamgeCodeUrl: "", //图形验证码的地址
      isRegister: false, // 控制注册组件是否显示
      flag: false, 
      // 返回的是注册用户信息
      RegisterUser: {
        name: "",
        pass: "",
        confirmPass: "",
        mobile: "",
        imageCode: "",//用户输入的图片验证码
      },
      // 用户信息校验规则,validator(校验方法),trigger(触发方式),blur为在组件 Input 失去焦点时触发
      rules: {
        // 这里的属性值,是prop的值
        name: [{ validator: validateName, trigger: "blur" }],
        pass: [{ validator: validatePass, trigger: "blur" }],
        confirmPass: [{ validator: validateConfirmPass, trigger: "blur" }],
        mobile: [{ validator: validateMobile, trigger: "blur" }],
        imageCode: [{validator: validateImageCode, trigger: "blur"}],
      },
    };
  },

  watch: {
    // 监听父组件传过来的register变量,设置this.isRegister的值
    register: function (val) {
      if (val) {
        this.isRegister = val;
      }
    },
    // 监听this.isRegister变量的值,更新父组件register变量的值
    isRegister: function (val) {
      if (!val) {
        this.$refs["ruleForm"].resetFields();
        this.$emit("fromChild", val);
      }
    },
  },
  mounted(){
    // DOM节点刚刚完成挂载,生成默认的图形验证码
    this.genImageCode()
  },
  methods: {
    
    // 生成图片验证码地址
    genImageCode(){
      // 生成一个uuid
      this.imageCodeID = uuid4(),
      // 生成一个图片验证码地址
      this.iamgeCodeUrl = "/users/image_code/" + this.imageCodeID + "/"
    },

    // 用户注册
    Register() {
      // 通过element自定义表单校验规则,校验用户输入的用户信息
      this.$refs["ruleForm"].validate((valid) => {
        //如果通过校验开始注册
        if (valid) {
          this.$axios
            .post("/users/register/", {
              userName: this.RegisterUser.name,
              pwd: this.RegisterUser.pass,
              mobile: this.RegisterUser.mobile,
              agree: this.aggree,
            })
            .then((res) => {
              // 200代表注册成功,其他的均为失败
              if (res.data.code == 200) {
                // 隐藏注册组件
                this.isRegister = false;
                // 弹出通知框提示注册成功信息
                this.notifySucceed(res.data.msg);
              } else {
                // 弹出通知框提示注册失败信息
                this.notifyError(res.data.msg);
              }
            })
            .catch((err) => {
              return Promise.reject(err);
            });
        } else {
          return false;
        }
      });
    },
  },
};
</script>

2.登录视图 (element-ui)

  • 使用v-model绑定表单,获取用户信息
  • 点击登录按钮,执行登录方法
  • axios的post请求提交参数

  • 接收响应

  • 登录成功,状态保持

<div id="myLogin">
    <el-dialog title="登录" width="300px" center :visible.sync="isLogin">
      <el-form
        :model="LoginUser"
        :rules="rules"
        status-icon
        ref="ruleForm"
        class="demo-ruleForm"
      >
        <!-- 用户名 -->
        <el-form-item prop="name">
          <el-input
            prefix-icon="el-icon-user-solid"
            placeholder="请输入账号"
            v-model="LoginUser.name"
          ></el-input>
        </el-form-item>
        
        <!-- 用户密码 -->
        <el-form-item prop="pass">
          <el-input
            prefix-icon="el-icon-view"
            type="password"
            placeholder="请输入密码"
            v-model="LoginUser.pass"
          ></el-input>
        </el-form-item>
        
        <!-- 点击登录 -->
        <el-form-item>
          <el-button
            size="medium"
            type="primary"
            @click="Login"
            style="width: 100%"
            >登录</el-button
          >
        </el-form-item>
      </el-form>
    </el-dialog>
  </div>


<script>
import { mapActions } from "vuex";

export default {
  
  // 登录组件
  name: "MyLogin",
  data() {
    // 用户名的校验方法
    let validateName = (rule, value, callback) => {
      if (!value) {
        return callback(new Error("请输入用户名"));
      }
      // 用户名以字母开头,长度在5-16之间,允许字母数字下划线
      const userNameRule = /^[a-zA-Z][a-zA-Z0-9_]{4,15}$|^1\d{10}$/;
      // 正则测试输入的用户名
      if (userNameRule.test(value)) {
        // 选择节点,设置验证通过
        this.$refs.ruleForm.validateField("checkPass");
        return callback();
      } else {//用户名验证未通过
        return callback(new Error("字母开头,长度5-16之间,允许字母数字下划线"));
      }
    };
    // 密码的校验方法
    let validatePass = (rule, value, callback) => {
      if (value === "") {
        return callback(new Error("请输入密码"));
      }
      // 密码以字母开头,长度在6-18之间,允许字母数字和下划线
      const passwordRule = /^[a-zA-Z]\w{5,17}$/;
      if (passwordRule.test(value)) {
        this.$refs.ruleForm.validateField("checkPass");
        return callback();
      } else {//密码验证未通过
        return callback(
          new Error("字母开头,长度6-18之间,允许字母数字和下划线")
        );
      }
    };
    return {
      LoginUser: {
        name: "",
        pass: ""
      },
      // 用户信息校验规则,validator(校验方法),trigger(触发方式),blur为在组件 Input 失去焦点时触发
      rules: {
        name: [{ validator: validateName, trigger: "blur" }],
        pass: [{ validator: validatePass, trigger: "blur" }]
      }
    };
  },
  computed: {
    // 获取vuex中的showLogin,控制登录组件是否显示
    isLogin: {
      get() {
        return this.$store.getters.getShowLogin;
      },
      set(val) {
        this.$refs["ruleForm"].resetFields();
        this.setShowLogin(val);
      }
    }
  },
  methods: {
    //将store中的actions方法映射到methods
    ...mapActions(["setUser", "setShowLogin"]),

    // 点击登录触发
    Login() {
      // 通过element自定义表单校验规则,校验用户输入的用户信息
      this.$refs["ruleForm"].validate(valid => {
        //如果通过校验开始登录
        if (valid) {

          // 发送ajax
          this.$axios.post("/users/login/", {
              user: this.LoginUser.name,
              pwd: this.LoginUser.pass,
              // withCredentials: true,
            })
            .then(res => {
              console.log("@@登录的响应:", res)
              // 200代表登录成功,其他的均为失败
              if (res.data.code == 200) {
                // res.data为后端响应的json
                // 隐藏登录组件
                this.isLogin = false;
                // 登录信息存到本地缓存
                let user = JSON.stringify(res.data.user);
                console.log("@@user", user)

                // 前端存储用户信息,表示登录成功
                localStorage.setItem("user", user);
                localStorage.setItem("token", res.data.token);
                // sessionStorage.setItem("")
                // 登录信息存到vuex,控制页面欢迎信息
                // console.log("@@res.data.user", res.data.user)
                this.setUser(res.data.user);
                // 弹出通知框提示登录成功信息
                this.notifySucceed(res.data.msg);
              } else {//响应不是200
                // 清空输入框的校验状态
                this.$refs["ruleForm"].resetFields();
                // 弹出通知框提示登录失败信息
                this.notifyError(res.data.msg);
              }
            })
            .catch(err => {
              console.log(err)
              return Promise.reject(err);
            });
        } else {//未通过用户校验
          return false;
        }
      });
    }
  }
};
</script>

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值