登录功能的实现(包括前后端)

news2025/2/27 22:56:26

目录:

    • 🍉🍉🍉UPDATE:
    • ✨✨✨前言
    • ✨✨✨概述
    • ✨✨✨技术栈
    • ✨✨✨效果图
    • ✨✨✨代码
    • ✨✨✨github链接


🍉🍉🍉UPDATE:

在这里插入图片描述
突然发现喜提榜一,蟹蟹读者老爷们的支持(づ ̄ 3 ̄)づ

润色了一些格式,加了一些emoji,还配上了一些颜色,希望能在枯燥的技术学习中多一些色彩。

(看了学弟的博客之后发现这样读下去的欲望会大大增加,于是就借鉴了一些hh,在这里强烈推荐一下他的博客:

阳树阳树

是个很厉害,进步超快的博主呢!


✨✨✨前言

登录功能对于前端刚入门不久的我来说较为困难,花了很久查阅了许多资料。代码部分涉及的技术栈和细节也很多。
过了一段时间来看,发现对自己写的代码竟然感觉十分陌生,故想写篇博客,对整个过程进行梳理。方便自己重看代码,同时也希望能帮到在实现这一功能受阻的程序猿



✨✨✨概述


登录功能的实现大致可分成6步:

1.前端验证用户输入是否符合规范,并将账号密码用公钥进行加密

2. 前端调用后端登录接口,向后端发起登录请求

3. 后端收到请求,通过私钥解密后查验数据库中是否有相应账号以及密码是否正确

4.验证通过后,将成功信息连同token一起发送给前端

5.前端将token储存起来,每次用户进入需要登录才能访问的页面时或者每次进入网站时向后端发送token

6. 若过期,则清除用户信息,回到未登录状态



✨✨✨技术栈

  • 前端:JavaScript,Vue

    • axios (发送请求的第三方库)

    • element-plus (基于 Vue 3 的组件库,简化UI代码)


  • 后端:Node.js

    • express (简化请求响应代码)
    • cors (解决跨域问题)
    • bcryptjs(密码加密解密)
    • jsonwebtoken(实现token的生成与验证)



✨✨✨效果图

登陆前
在这里插入图片描述


登录界面
在这里插入图片描述

登录成功

在这里插入图片描述


✨✨✨代码

表单验证及发送请求

<template>
  <el-form
    ref="ruleFormRef"
    :model="ruleForm"
    :rules="rules"
    label-width="35rem"
  >
    <el-form-item prop="phone_number" class="phone_number">
      <el-input
        type="text"
        placeholder="你的手机号/邮箱"
        v-model="ruleForm.phone_number"
      />
    </el-form-item>

    <el-form-item prop="password" class="password">
      <el-input
        type="password"
        placeholder="密码"
        v-model="ruleForm.password"
        autocomplete="off"
      />
    </el-form-item>

    <el-form-item class="login_in_button">
      <el-button type="primary" @click="submitForm(ruleFormRef)"
        >登录</el-button
      >
      <el-button>注册</el-button>
    </el-form-item>
  </el-form>
</template>

<script lang="ts" setup>
import { reactive, ref } from "vue";
import type { FormInstance } from "element-plus";
import { ElMessage } from "element-plus";
import { LoginInPost } from "../../api/LoginIn";
import { useRouter } from "vue-router";
import axios from "axios";
import { useStore } from "../../../store.js";
import { storeToRefs } from "pinia";

const router = useRouter();
const store = useStore();
// const { islogin } = storeToRefs(store);

const formSize = ref("default");
const ruleFormRef = ref<FormInstance>();
const ruleForm = reactive({
  phone_number: "",
  password: "",
});

const checkPhonenumber = (rule: any, value: string, callback: any) => {
  if (!value) {
    callback(new Error("请输入注册时用的邮箱或者手机号呀"));
  }
  let re = /^[0-9]*$/;
  // console.log(value.length);
  // ||value.length != 11
  if (!re.test(value)) {
    // console.log(re.test(value));
    callback(new Error("输入格式有误哟"));
  } else {
    callback();
  }
};

const checkPassword = (rule: any, value: string, callback: any) => {
  if (!value) {
    return callback(new Error("请输入密码呀"));
  }
  let re = /^[0-9a-zA-Z]*$/;

  if (!re.test(value)) {
    // console.log(re.test(value));
    return callback(new Error("输入格式有误哟"));
  } else {
    return callback();
  }
};

const rules = reactive({
  phone_number: [{ validator: checkPhonenumber, trigger: "blur" }],
  password: [{ validator: checkPassword, trigger: "blur" }],
});

const submitForm = async (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  await formEl.validate((valid, fields) => {
    if (valid) {
      let url = "http://localhost:3007/api/user_register";
      let dataObject = reactive({
        phoneNumber: ruleForm.phone_number,
        password: ruleForm.password,
      });
      try {
        LoginInPost(dataObject).then(function (res) {
          // console.log(res.data.token);
          if (res.data.status == 0) {
            store.loginIn();
            localStorage.setItem("token",res.data.token);
            router.go(-1);
          } else if (res.data.status == 403) {
            ElMessage({ message: res.data.message, offset: 200 });
          }
        });
      } catch (error) {
        console.log(error);
      }
    } else {
      console.log("error submit!", fields);
    }
  });
};
</script>

axios封装

index.js

import db from "../db/user_db.js"
import bcrypt from "bcryptjs"
import jwt from "jsonwebtoken"
import config from "../config.js"

export async function regUser(req, res) {
    try {
        // console.log(req.body);
        const data = req.body;
        // console.log(data.phoneNumber);
        const selectSql = "select * from users where phoneNumber=?";
        let [result] = await db.query(selectSql, data.phoneNumber)
            .catch(err => { console.log(err) });
        // console.log(result.length);
        //号码已占用
        if (result.length > 0) {
            throw new Error(res.send({
                status: 403,
                message: "电话号码已注册过了嗷",
            }))
        }

        //对密码进行加密
        // console.log(data.password);
        data.password = bcrypt.hashSync(data.password, 10);
        console.log(data.password);
        const insertSql = "insert into users set ?";

        const insertStr = { phoneNumber: data.phoneNumber, password: data.password };
        [result] = await db.query(insertSql, insertStr)
            .catch(err => { console.log(err) });
        // console.log(result.affectedRows);
        if (result.affectedRows != 1) {
            res.send({
                status: 403,
                message: "请求失败",
            })
        }
        res.send({
            status: 0,
            message: "请求成功",
        })
    } catch (err) {
        res.send({
            status: 403,
            message: "请求失败",
            desc: err.message
        })

    }


}

export async function loginUser(req, res) {
    try {
        // console.log(req.body);
        const data = req.body;
        // console.log(data.phoneNumber);
        const selectSql = "select * from users where phoneNumber=?";
        let [result] = await db.query(selectSql, data.phoneNumber)
            .catch(err => { console.log(err) });
        // console.log(result);
        //无注册账号
        if (result.length == 0) {
            return res.send({
                status: 403,
                message: "无注册账号",
            })
        }

        //验证密码
        // console.log(result[0].password);
        const compareResult = bcrypt.compareSync(data.password, result[0].password);
        if (compareResult == 0) {
            return res.send({
                status: 403,
                message: "密码错误",
            })
        }

        const user = { ...result[0], password: '' };
        console.log(user);
        const tokenStr = jwt.sign(user, config.jwtSecretKey, { expiresIn: "10h" });
        return res.send({
            status: 0,
            message: "登录成功",
            token: tokenStr
        })
    } catch (err) {
        return res.send({
            status: 403,
            message: "请求失败",
            desc: err.message
        })

    }
}

export async function GetInfo(req, res) {
    console.log(req.headers.authorization);
    const token = req.headers.authorization;
    if (!token) {
        return res.send({
            status: 403,
            message: "无token"
        })
    }
    try{
        const flag = jwt.verify(token, config.jwtSecretKey);
        if (flag) {
            return res.send({
                status: 0,
                message: "验证成功"
            })
        } else {
            return res.send({
                status: 403,
                message: "token错误"
            })
        }
    }catch(err){
        return res.send({
            status: 403,
            message: "token已过期"
        })
    }
    
}


request.js

// http/axios.js
import instance from "./index"
/**
 * @param {String} method  请求的方法:get、post、delete、put
 * @param {String} url     请求的url:
 * @param {Object} data    请求的参数
 * @param {Object} config  请求的配置
 * @returns {Promise}     返回一个promise对象,其实就相当于axios请求数据的返回值
 */

export const axios = ({
    method,
    url,
    data,
    config
}) => {
    method = method.toLowerCase();
    if (method == 'post') {
        return instance.post(url, data, {...config})
    } else if (method == 'get') {
        return instance.get(url, {
            params: data,
            ...config
        })
    } else if (method == 'delete') {
        return instance.delete(url, {
            params: data,
            ...config
        }, )
    } else if (method == 'put') {
        return instance.put(url, data,{...config})
    } else {
        console.error('未知的method' + method)
        return false
    }
}

Login.js

import { axios } from "../utils/request"

export const LoginInPost = (data) => {
    return axios({
        url: "http://localhost:3007/api/user_login",
        method: "post",
        data
    })
}

export const GetUsersInfo = (data) => {
    return axios({
        url: "http://localhost:3007/api/get_info",
        method: "get",
        data
    })
}

登录验证

export async function loginUser(req, res) {
    try {
        // console.log(req.body);
        const data = req.body;
        // console.log(data.phoneNumber);
        const selectSql = "select * from users where phoneNumber=?";
        let [result] = await db.query(selectSql, data.phoneNumber)
            .catch(err => { console.log(err) });
        // console.log(result);
        //无注册账号
        if (result.length == 0) {
            return res.send({
                status: 403,
                message: "无注册账号",
            })
        }

        //验证密码
        // console.log(result[0].password);
        const compareResult = bcrypt.compareSync(data.password, result[0].password);
        if (compareResult == 0) {
            return res.send({
                status: 403,
                message: "密码错误",
            })
        }

        const user = { ...result[0], password: '' };
        console.log(user);
        const tokenStr = jwt.sign(user, config.jwtSecretKey, { expiresIn: "10h" });
        return res.send({
            status: 0,
            message: "登录成功",
            token: tokenStr
        })
    } catch (err) {
        return res.send({
            status: 403,
            message: "请求失败",
            desc: err.message
        })

    }
}



✨✨✨github链接

想要看完整版代码以及页面效果,请移步github哦
https://github.com/Ki-Wi-Berry/bilibili-videos


⛄码字不易,如果觉得对您有帮助的话,麻烦点个免费的赞~⛄

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

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

相关文章

微信小程序完整项目实战(前端+后端)

基于微信小程序的在线商城点单系统 前言&#xff1a;闲来无事&#xff0c;想以后自己开一个小超市或者小吃店&#xff0c;能够支持线上下单&#xff0c;既方便客户也方便自己。系统采用C#语言作为后端实现与小程序的交互&#xff0c;给用来学习或者想自己开个小店的朋友当个参考…

如何在 WordPress 中嵌入 iFrame

&#x1f482; 个人网站:【海拥】【摸鱼游戏】【神级源码资源网站】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 想寻找共同学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】&#x1f4ac; 免费且…

【uniapp小程序开发】—— 组件封装之【自定义轮播图】

文章目录&#x1f34b;前言&#xff1a;&#x1f34d;正文1、首先了解swiper组件1.1、小小的demo示例&#xff1a;1.2、自定义轮播图效果展示说明2、完成自定义轮播图效果3、组件封装——自定义轮播图3.1、创建swiper-doc.vue组件3.2、组件调用&#xff0c;封装完成&#x1f38…

session、cookie、token的区别?

前言 今天就来理一理session、cookie、token这三者之间的关系&#xff01; 1.为什么会有它们&#xff1f; 我们都知道 HTTP 协议是无状态的&#xff0c;所谓的无状态就是客户端每次想要与服务端通信&#xff0c;都必须重新与服务端链接&#xff0c;意味着请求一次客户端和服…

React组件通信-父子组件间的通信

文章目录React父子组件通信认识组件嵌套组件通信父传子参数验证子传父React父子组件通信 认识组件嵌套 组件之间存在嵌套关系&#xff1a; 在之前的案例中&#xff0c;我们只是创建了一个组件App&#xff1b; 如果我们一个应用程序将所有的逻辑都放在一个组件中&#xff0c;那…

适合我的前端学习路线(学习前端不迷路)

适合我的前端学习路线&#xff08;学习前端不迷路&#xff09; 小伙伴们想学习前端&#xff0c;但是却不知如何入手&#xff0c;上网查前端学习路线&#xff0c;第一页往往充斥着各种培训公司的广告&#xff0c;又或者是搜前端学习路线图时&#xff0c;出现大量的路线图导致你还…

【学Vue就跟玩一样】Vue中的路由与多种守卫

1.vue-routervue的一个插件库&#xff0c;专门用来实现对SPA应用的单页Web应用(single page web application, SPA)。整个应用只有一个完整的页面。点击页面中的导航链接不会刷新页面&#xff0c;只会做页面的局部更新。数据需要通过ajax请求获取。2.什么是路由一个路由就是一组…

Tomcat服务器部署+Web项目搭建

Tomcat服务器部署&#xff0b;Web项目搭建 1.Tomcat服务器 1 . Tomcat下载 2 . Tomcat启动与关闭 3 . 配置端口号2.IDEA搭建Web项目 1 . web项目创建 2 . 本地服务器配置 Tomcat下载 tomcat官网&#xff1a;tomcat 1.选择与自己电脑对应的位数下载&#xff0c;我们…

HTML跳动爱心代码|最近很火的爱心代码你还没收到吗

最近很火的跳动爱心代码HTML实现可DIY 看效果 QQ录屏20221108115545点个赞吧,养成好习惯 不想动手的小伙伴可以直接&#x1f447;&#x1f447; 阿里云盘直接提取 阿里云盘 DIY版----提取码: f30q 使用说明 主体代码来源于网络,让我们为原作者点赞&#x1f44d;我用css3的anim…

云服务器部署 Web 项目

一: 搭建 Java 部署环境1: 安装 JDK2: 安装 Tomcat总结3: 安装 MySQL(1): 依次安装(2): 更改配置(3): 启动(4): 测试连接二: 部署 web 项目1: 给服务器准备好依赖的数据2: 微调我们的 Java 代码3: 重新打包4: 上传到服务器上5: 验证一: 搭建 Java 部署环境 之前说过 yum这个命…

手把手教你安装VSCode(附带图解步骤)

一、前端工具vscode 1.1、概述 前端开发是创建Web页面或app等前端界面呈现给用户的过程&#xff0c;通过HTML&#xff0c;CSS及JavaScript以及衍生出来的各种技术、框架、解决方案&#xff0c;来实现互联网产品的用户界面交互 [1] 。它从网页制作演变而来&#xff0c;名称上有…

Node安装及配置

目录一、Node安装二、Node环境配置2.1 下载国内淘宝镜像三、下载Node.js项目一、Node安装 首先我们进入Node的官网链接: 下载链接 点击下载&#xff0c;选择自己对应的版本&#xff0c;博主这边使用的Windows 安装包 (.msi) 下载之后双击运行下载包点击下一步 点击change 安装完…

【Vue】悬浮窗和聚焦登录组件经验总结

前言 ​ 本文整理了实现悬浮窗以及聚焦登录组件的功能。 ​ 为的是方便大家和自己的学习。 ​ 省流&#xff1a;可以只看1.2 和 2的代码即可 1 悬浮窗 现在各大流行视频网站的平台都在使用这种悬浮显示的效果&#xff0c;我就想这种东西是怎样搞出来的呢&#xff01;几经尝…

Vue项目保持用户登录状态(localStorage + vuex 刷新页面后状态依然保持)

在前端项目开发中&#xff0c;实现用户的登陆注册功能时常常会有一个问题&#xff0c;那就是我们设置的登录状态&#xff0c;在浏览器页面刷新后就消失了&#xff0c;这其实只是因为我们没有保存用户状态。 这里小马演示使用的是 localStorage vuex 方法&#xff08;其他诸如…

SpringBoot+Vue实现简单用户管理平台第一篇(后端接口设计)

&#x1f680; 注重版权&#xff0c;转载请注明原作者和原文链接&#x1f96d; 作者&#xff1a;Yuan-Programmer&#x1f34e; 主页&#xff1a;https://blog.csdn.net/weixin_47971206/article/details/121368075?spm1001.2014.3001.5502&#x1f349; 进来的小伙伴点点赞呀…

nuxt3:我们开始吧-开发-配置-部署

一、背景介绍 2022 年 11 月 16 日&#xff0c;全球最大的 Nuxt 会议 Nuxt Nation 2022 在线举行&#xff0c;并正式发布了 Nuxt.js 3.0 的第一个稳定版本。Nuxt 3 是基于 Vite、Vue3 和 Nitro 的 Nuxt 框架的现代重写&#xff0c;具有一流的 Typescript 支持&#xff0c;是两年…

前端基础从头学——VsCode使用教程+html基础(入门篇)

作者简介&#xff1a;hello&#xff01;大家好&#xff0c;初学前端知识&#xff0c;请多多指教。 希望我的分享能够帮助到更多的人&#xff0c;如果觉得我的分享有帮助的话&#xff0c;请大家一键三连支持一下哦~ 文章目录前言一、VsCode编辑器使用教程1、VsCode的下载与安装2…

HTML+CSS+JS+Jquery+练手项目+...合集(前端学习必备,持续更新中...)

非常实用的教程案例&#xff0c;均有详细的步骤讲解&#xff0c;不懂得可以私信博主&#xff0c;看到就会回的&#xff01;未来将继续更新Vue&#xff0c;React等更多前端知识&#xff0c;欢迎收藏关注&#xff01; 文章目录一、HTML二、CSS三、JavaScript四、jQuery五、 前端项…

PromiseA+规范之手写Promise

前言 1、Promise 的意义&#xff1f; 在javascript的世界中&#xff0c;所有代码都是单线程执行的&#xff0c;由于这个“缺陷”&#xff0c;导致JavaScript的所有网络操作&#xff0c;浏览器事件&#xff0c;都必须是异步执行。Ajax可以解决这个问题&#xff0c;但是并不好复用…

【flask进阶】Flask实现自定义分页(python web通用)

📋 个人简介 💖 作者简介:大家好,我是阿牛,全栈领域新星创作者。😜🎉 支持我:点赞👍+收藏⭐️+留言📝📣 系列专栏:flask框架快速入门🍁💬格言:要成为光,因为有怕黑的人!🔥 目录 📋 个人简介前言后端后端思路后端代码前端前端思路前端代码