Vue+NodeJS+MongoDB实现邮箱验证注册、登录

一.主要内容

  • 邮件发送
  • 用户注册
  • 用户信息存储到数据库
  • 用户登录
  • 密码加密
  • JWT生成token
  • Cookie实现快速登录

在用户注册时,先发送邮件得到验证码.后端将验证进行缓存比对,如果验证码到期,比对不正确,拒绝登录;如果比对正确,将用户的信息进行加密存储到数据库.

用户登录时,先通过用户名去数据库查询是否存在该用户,如果不存在发送信息提示前端,如果存在.将用户输入的密码和将数据库中解密成功后的密码进行比对.如果成功,将用户的唯一id生成token与用户的信息一起发送给前端.前端将token存入Cookie中.

在一定时间内,用户第二次登录时.先去获取Cookie,将token解析出来,发送给后端,后端进行token解密,得到用户的id,通过id去数据库查询用户数据,并返回给前端.

二.MongoDB数据库配置及连接

1.配置

推荐看大佬写的

MongoDBicon-default.png?t=N7T8http://t.csdn.cn/f2yzh

版本推荐4以上,这里使用的是4.4.23

配置好后,我们创建一个数据库,为这个数据库添加一个用户.

db.createUser({user:"用户名", pwd: "用户密码", roles: [{role: "dbOwner", db: "数据库名"}]})

我们接下来连接这个数据库.

2.下载mongoose

npm i mongoose

3.database.js

const mongoose = require('mongoose'); 	const connect='mongodb://数据库用户名:数据库用户密码@127.0.0.1:27017/数据库名'
mongoose.connect(connect, {useNewUrlParser: true,useUnifiedTopology: true,
});
//创建实例
const db = mongoose.connection;
//数据库连接
db.on('error', console.error.bind(console, '连接错误:'));
db.once('open', function () {console.log('成功连接到数据库');
});//定义用户模型
...// 关闭连接(在程序退出时)
process.on('SIGINT', async function () {try {await mongoose.connection.close();console.log('数据库连接已关闭');process.exit(0);} catch (error) {console.error('关闭数据库连接时出错:', error);process.exit(1);}});module.exports = {db}

4.创建用户模型

用户信息包括

_id:数据库自动生成

username:用户名,唯一不可出现同样的用户名,

email:邮箱名,唯一不可出现同样的邮箱名,

password:密码,加密

...其他属性可以自定义添加

安装bcrypt,一种加密算法

npm i bcrypt
...连接// 定义用户模型
const userSchema = new mongoose.Schema({username:{type:String, unique:true}, //唯一,防止命名重复password:{type:String, set(val) //val是需要加密的值{return require('bcrypt').hashSync(val,10) //10加密强度}},  //加密email:{type:String,unique:true},});// 添加新的属性---如果后续有新的属性加入没有可以省略//userSchema.add({//  avatar_url: {type:String}//});//创建用户模型(集合)const User = mongoose.model('User', userSchema); ...关闭连接module.exports = {db,User}

至此数据库的配置连接就完成了

三.后端搭建

1.安装第三方包

npm install express --savenpm i body-parsernpm i cors

2.index.js

const express = require('express')
const  router = require('./router'); 		
const bodyParser = require('body-parser')		
const cors = require('cors');			
const app = express()app.use(cors()) //跨域处理
app.use(bodyParser.json())	 //解析请求体// 使用路由文件
app.use('/',router);app.listen(3000, () => {console.log('server running ...');
})

3.router.js

邮件发送我以及分离出来了,可以借鉴我的这篇博客.与这篇博客是对接上的.

Vue+NodeJS实现邮件发送icon-default.png?t=N7T8http://t.csdn.cn/OWAgl

 唯一要修改的地方就是,一个邮箱注册一个账号,你也可以自定义多少个

//发送邮件router.get('/getemail', async (req, res) => {try {const { mail } = req.query;// 验证邮箱是否存在const email = await User.findOne({ email: mail });if (email) {return res.status(422).json({ message: '邮箱存在账号' });}// 随机验证码// 将code存入缓存//发送邮件} catch (error) {});

安装jsonwebtoken

npm i jsonwebtoken

 一些数据密钥可以存储在一个.env文件中,可以参考这篇博客

Vue+NodeJS上传图片到腾讯云Cosicon-default.png?t=N7T8http://t.csdn.cn/cwN0N

const express = require('express');
const router = express.Router();
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const {User,db}=require('./database');	const SECRET=*******... //密钥,鉴权登录的时候来解析token//注册//登录//鉴权登录//邮件发送module.exports = router

3-1.注册

注册前,我们先发送邮件得到验证码,将验证码缓存.再请求注册接口的时候,先验证验证码的准确性.

如果错误或者过期了,不进行注册.


//用户注册
router.post('/register',async(req,res)=>{// 获取缓存---邮箱验证码let cachedValue = myCache.get("code");//解构出来前端传过来的信息let {code,username,password,email}=req.query.infoif(!cachedValue){return res.status(422).send({ message: '验证码过期' });}if(code!=cachedValue){return res.status(422).send({ message: '验证码输入错误' });}try {// 验证成功后,插入数据const user = await User.create({username: username,password: password,email: email,});myCache.del("code");//将用户信息发送给前端res.send(user);} catch (error) {console.error(error);res.status(422).send({message: '该用户已经存在'});}})

3-2.登录

这里我们是将用户的唯一_id生成token发给前端,前端将token存储到Cookie中,下次登录的时候将token发送给鉴权接口,读取token得到用户的_id.通过_id去查询数据库,得到用户的信息,实现登录.我们给Cookie设置时效性,在一定时间内,快速登录.

//用户登录
router.post('/login', async (req, res) => {try {let { username, password } = req.query.info;// 查询用户是否存在const user = await User.findOne({ username });// 用户名不存在if (!user) {return res.status(422).json({ message: '用户名不存在' });}// 校验密码//true或falseconst isPasswordValid = bcrypt.compareSync(password, user.password);if (!isPasswordValid) {return res.status(422).json({ message: '密码无效' });}// 生成 JWTconst token = jwt.sign({ id: String(user._id) }, SECRET); //密钥res.json({ //发送json数据user,token});} catch (error) {console.log(error)}})

3-3.鉴权登录

由于官方的规范,前端传过来的token格式是

所以将authorization切成两部分,取后面

//用户鉴权
router.get('/profile',async(req,res)=>{try {const rawToken = String(req.headers.authorization).split(' ').pop();const { id } = jwt.verify(rawToken, SECRET);const user = await User.findById(id);if (!user) {return res.status(404).json({ message: '用户不存在' });}res.json({user}); // 使用 .json() 方法发送 JSON 数据} catch (error) {console.log(error)}
})

至此我们的后端服务搭建完成

四.Vue前端

1.封装axios

这里我们将axios进行封装,多个请求时方便书写

1-1.http.js

import axios from "axios";const http = axios.create({baseURL: 'http://127.0.0.1:3000', // 注意这里的双斜杠timeout: 5000
});// 请求拦截器
http.interceptors.request.use(config => {return config;
}, error => {return Promise.reject(error);
});// 响应拦截器
http.interceptors.response.use(response => {return response.data;
}, error => {return Promise.reject(error);
});export default http;

1-2.api.js

import http from './http';//注册
export async function registerAPI(info) {const response = await http({url: 'register',method:'post',params: {info}});return response; // 返回响应数据}//登录
export async function loginAPI(info) {const response = await http({url: 'login',method:'post',params: {info}});return response; // 返回响应数据}//鉴权登录
export async function getTokenAPI(config) {const response = await http({url: 'profile',method:'get',// 自定义请求头headers:{authorization:config.authorization}});return response; // 返回响应数据}//邮箱

2.请求

我们先进行输入内容的去空格处理和为空的判断.

如果没有输入邮箱,不能发送邮件.

如果用户名,密码,邮箱,验证码有一项为空,不能注册.

2-1.发送邮件

...

2-2.注册

import {registerAPI} from './api'...// 注册function signUp(){
//去除空格
...
//判断是否为空
...let registerInfo={username:username.value,password:password.value,email:email.value,code:code.value}registerAPI(registerInfo).then(response => {console.log('Register successful:', response);}).catch(error => {// 处理错误console.log(error)});}

2-3.登录

在登录成功后,将token存储到Cookie中,设置存储时间.

import {loginAPI} from './api'
import useCookie from './Cookie';
const { setCookie } = useCookie();...//登录
function SignIn(){
//去除空格
username.value=username.value.replace(/\s/g, '')
password.value=password.value.replace(/\s/g, '')//判断是否为空
if(username.value==''||password.value==''){console.log('格式不正确')
}else{
const loginInfo={username:username.value,password:password.value}loginAPI(loginInfo).then(response => {setCookie('token', response.token, 0.5); // 存储30分钟console.log(response)}).catch(error => {// 处理错误console.log(error)});
}}

 

 

2-4.Cookie封装        


export default function useCookie() {//获取Cookieconst getCookie = (name) => {const value = `; ${document.cookie}`;const parts = value.split(`; ${name}=`);if (parts.length === 2) {return parts.pop().split(';').shift();}};//设置Cookieconst setCookie = (name, value, hours) => {const date = new Date();date.setTime(date.getTime() + (hours * 60 * 60 * 1000));const expires = `expires=${date.toUTCString()}`;document.cookie = `${name}=${value}; ${expires}; path=/`;};//销毁Cookieconst deleteCookie=(name)=>{// 将Cookie的过期时间设置为过去的日期document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;}return {getCookie,setCookie,deleteCookie};
}

 

2-5.鉴权登录

import {getTokenAPI} from './api'
import useCookie from './Cookie';
const { getCookie } = useCookie();...onMounted( async()=>{
// 读取Token
const Token = getCookie('token');
const config = {authorization: `Bearer ${Token}`, // 设置Bearer Token};// 发送需要Token的请求
await getTokenAPI(config).then(response => {// 处理响应数据console.log(response)}).catch(error => {// 处理错误console.log(error)});
})

至此前端页面完成.

如果有不对或者优化的地方欢迎指正,如果使用时出现了问题,可以留言评论,大家一起解决!

这里的token设置存在bug,没有真正的销毁token.有时间改正,有熟悉的大佬可以评论留言,给出好的解决办法.感谢!

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

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

相关文章

[H5动画制作系列] Sprite及Text Demo

参考代码: sprite.js: var canvas, stage, container; canvas document.getElementById("mainView"); function init() {stage new createjs.Stage(canvas);createjs.Touch.enable(stage);var loader new createjs.LoadQueue(false);loader.addEventListener(&q…

Docker的开源容器镜像仓库Harbor安装

概述 Docker Hub是Docker官方提供的在线Docker镜像注册中心,其支持Docker镜像的查询(search)、提交(push)以及获取(pull)。目前,在云原生领域中,CNCF提供Harbor开源版本…

创建开机自启的脚本

在启动许多ros节点时有多种方式,我推荐使用launch来启动所有的节点,这也是一种规范的方式。以后会慢慢向这个方向靠。 除此之外还可以通过创建的脚本来启动: 脚本位置不限,只需要: sudo gedit xxx.sh在里面添加相应的…

UI设计师的发展前景是否超越了平面设计?

这是一个现代经济学的典型话题:应该跟随趋势追逐风口,还是坚守成熟的“夕阳产业” UI 设计行业发展短短不过 20 多年,但平面设计这个“夕阳产业”最早可以追溯到上世纪的二三十年代。显而易见的答案是,更新兴的 UI 设计师得到的好…

C语言_指针(1)

文章目录 前言一、指针数组1.1利用指针数组模拟出二维数组 二、数组指针2.1数组名是数组首元素的地址2.2 二维数组传参2.3 一级指针传参2.4 二级指针传参 三. 函数指针四 . typedef 重命名 前言 指针数组是由指针组成的数组。它的每个元素都是一个指针,可以指向任何…

java并发编程 ConcurrentLinkedQueue详解

文章目录 1 ConcurrentLinkedQueue是什么2 核心属性详解3 核心方法详解3.1 add(E e)3.2 offer(E e)3.3 poll()3.4 size()3.5 并发情况分析 4 总结 1 ConcurrentLinkedQueue是什么 ConcurrentLinkedQueue是一个无界的并发队列,和LinkedBlockingQueue相比&#xff0c…

对象临时中间状态的条件竞争覆盖

Portswigger练兵场之条件竞争 🦄条件竞争之对象临时中间状态的条件竞争 Lab: Partial construction race conditions🚀实验前置必要知识点 某些框架尝试通过使用某种形式的请求锁定来防止意外的数据损坏。例如,PHP 的本机会话处理程序模块…

【echarts】如何修改折线图X轴每个刻度的间隔宽度,让拥挤的空间变大,所有坐标点的文案可以显示得下,Echarts x轴文本内容太长的几种解决方案

Echarts 如何修改折线图X轴每个刻度的间隔宽度,让拥挤的空间变大,所有坐标点的文案可以显示得下,Echarts x轴文本内容太长的几种解决方案 有以下几种方案,堪称最全方案: 1、dataZoom进行坐标的比例缩放 通过调整dataZ…

uniapp 在 onLoad 事件中 this.$refs 娶不到的问题

现象 本人想在主页面加载的时候调用子组件的方法。示例代码如下: 运行,发现 this.$refs 取不到。如下图所示: 解决方法,把onLoad 换为 onReady 就可以了。

一笑的大型连续剧之第二集

开场白 各位小伙伴们大家晚上好,今天来和大家一起更新一下我的开发之旅的第二集。上周时间也已经匆匆过去了。今天也是周六晚上了,这个周末很充实但是又很空虚。 本周小结 本周完成了我开发旅途中的第一个模块,关于绩效面谈的一个模块的一…

亚马逊云科技与伊克罗德推出AI绘画解决方案——imAgine

在过去的数月中,亚马逊云科技已经推出了多篇介绍如何在亚马逊云科技上部署Stable Diffusion,或是如何结合Amazon SageMaker与Stable Diffusion进行模型训练和推理任务的内容。 为了帮助客户快速、安全地在亚马逊云科技上构建、部署和管理应用程序&#x…

Docker部署EMQX

1、简介 EMQ X (Erlang/Enterprise/Elastic MQTT Broker) 是基于 Erlang/OTP 平台开发的开源物联网 MQTT 消息服务器。 Erlang/OTP是出色的软实时 (Soft-Realtime)、低延时 (Low-Latency)、分布式 (Distributed)的语言平台。 MQTT 是轻量的 (Lightweight)、发布订阅模式 (Pu…

简明SQL条件查询指南:掌握WHERE实现数据筛选

条件查询是用于从数据库中根据特定条件筛选数据行的一种方式&#xff0c;它避免了检索整个表中的数据。通常&#xff0c;使用 WHERE 子句来定义过滤条件&#xff0c;只有符合这些条件的数据行才会被返回。 SQL中的运算符有&#xff1a;、!、<、> 等&#xff0c;用于进行…

Swift页面添加水印

本文主要讨论的是给图片或者视图添加全屏水印。比较常见的是添加单个水印,这个比较好处理,网络上也有很多参考的方法。本文实现的是铺满的全屏水印,具体参考效果如下: 实现思路: 1、根据水印文本以及相应样式生成水印图片,水印图大小根据文本计算而来 2、生成需要铺满水…

Java中如何获取一个字符串是什么类型

Java中如何获取一个字符串是什么类型&#xff1f; 在Java中&#xff0c;您可以使用一些方法来确定一个字符串的类型。下面是一些常用的方法&#xff1a; 使用正则表达式&#xff1a;您可以使用正则表达式来匹配字符串是否符合特定的模式或格式&#xff0c;以确定其类型。例如&…

nginx空字节漏洞复现

将nginx复制到C盘根目录 cmd运行startup.bat 安装完成后访问 输入info.php 输入info.png 抓包使用00截断 可以看到phpinfo成功执行 在PHP的底层C语言里&#xff0c;%00代表着字符串结束&#xff0c;00截断可以用来绕过后端验证&#xff0c;后端验证的时候因为00截断认为文件是…

C++中的红黑树

红黑树 搜索二叉树搜索二叉树的模拟实现平衡搜索二叉树(AVL Tree)平衡搜索二叉树的模拟实现红黑树(Red Black Tree)红黑树的模拟实现 红黑树的应用(Map 和 Set)Map和Set的封装 搜索二叉树 搜索二叉树的概念&#xff1a;二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&…

JAVA 的四种访问权限

在Java编程中&#xff0c;访问权限是非常重要的概念&#xff0c;因为它可以保证代码的安全性和封装性。访问权限有四种&#xff0c;分别是public、protected、default和private。 private&#xff1a;如果一个类的方法或者变量被private修饰&#xff0c;那么这个类的方法或者变…

【数据结构】红黑树的插入与验证

文章目录 一、基本概念1.时代背景2. 基本概念3.基本性质 二、实现原理1. 插入1.1变色1.2旋转变色①左旋②右旋③右左双旋④左右双旋 2.验证 源码总结 一、基本概念 1.时代背景 1972年鲁道夫拜尔(Rudolf Bayer)发明了一种数据结构&#xff0c;这是一种特殊的B树4阶情况。这些树…

在Photoshop上标小图标的操作记录

1、做小图标 收集背景图 的背景的rgb值 把这个rgb值记下来&#xff0c;上面的背景要用。 2、统一图标大小 宽度、高度&#xff0c;都设置成1.52 3、把图标往地图上拖 拖到背景图上&#xff0c;可以用上下左右键调整位置 4、在图片上写字 右键这个&#xff0c;就可以写字了。…