• 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

    1. const mongoose = require('mongoose');
    2. const connect='mongodb://数据库用户名:数据库用户密码@127.0.0.1:27017/数据库名'
    3. mongoose.connect(connect, {
    4. useNewUrlParser: true,
    5. useUnifiedTopology: true,
    6. });
    7. //创建实例
    8. const db = mongoose.connection;
    9. //数据库连接
    10. db.on('error', console.error.bind(console, '连接错误:'));
    11. db.once('open', function () {
    12. console.log('成功连接到数据库');
    13. });
    14. //定义用户模型
    15. ...
    16. // 关闭连接(在程序退出时)
    17. process.on('SIGINT', async function () {
    18. try {
    19. await mongoose.connection.close();
    20. console.log('数据库连接已关闭');
    21. process.exit(0);
    22. } catch (error) {
    23. console.error('关闭数据库连接时出错:', error);
    24. process.exit(1);
    25. }
    26. });
    27. module.exports = {db}

    4.创建用户模型

    用户信息包括

    _id:数据库自动生成

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

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

    password:密码,加密

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

    安装bcrypt,一种加密算法

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

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

    三.后端搭建

    1.安装第三方包

    1. npm install express --save
    2. npm i body-parser
    3. npm i cors

    2.index.js

    1. const express = require('express')
    2. const router = require('./router');
    3. const bodyParser = require('body-parser')
    4. const cors = require('cors');
    5. const app = express()
    6. app.use(cors()) //跨域处理
    7. app.use(bodyParser.json()) //解析请求体
    8. // 使用路由文件
    9. app.use('/',router);
    10. app.listen(3000, () => {
    11. console.log('server running ...');
    12. })

    3.router.js

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

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

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

    1. //发送邮件
    2. router.get('/getemail', async (req, res) => {
    3. try {
    4. const { mail } = req.query;
    5. // 验证邮箱是否存在
    6. const email = await User.findOne({ email: mail });
    7. if (email) {
    8. return res.status(422).json({ message: '邮箱存在账号' });
    9. }
    10. // 随机验证码
    11. // 将code存入缓存
    12. //发送邮件
    13. } catch (error) {
    14. });

    安装jsonwebtoken

    npm i jsonwebtoken

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

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

    1. const express = require('express');
    2. const router = express.Router();
    3. const bcrypt = require('bcrypt');
    4. const jwt = require('jsonwebtoken');
    5. const {User,db}=require('./database');
    6. const SECRET=*******... //密钥,鉴权登录的时候来解析token
    7. //注册
    8. //登录
    9. //鉴权登录
    10. //邮件发送
    11. module.exports = router
    3-1.注册

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

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

    1. //用户注册
    2. router.post('/register',async(req,res)=>{
    3. // 获取缓存---邮箱验证码
    4. let cachedValue = myCache.get("code");
    5. //解构出来前端传过来的信息
    6. let {code,username,password,email}=req.query.info
    7. if(!cachedValue){
    8. return res.status(422).send({ message: '验证码过期' });
    9. }
    10. if(code!=cachedValue){
    11. return res.status(422).send({ message: '验证码输入错误' });
    12. }
    13. try {
    14. // 验证成功后,插入数据
    15. const user = await User.create({
    16. username: username,
    17. password: password,
    18. email: email,
    19. });
    20. myCache.del("code");
    21. //将用户信息发送给前端
    22. res.send(user);
    23. } catch (error) {
    24. console.error(error);
    25. res.status(422).send({
    26. message: '该用户已经存在'
    27. });
    28. }
    29. })
    3-2.登录

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

    1. //用户登录
    2. router.post('/login', async (req, res) => {
    3. try {
    4. let { username, password } = req.query.info;
    5. // 查询用户是否存在
    6. const user = await User.findOne({ username });
    7. // 用户名不存在
    8. if (!user) {
    9. return res.status(422).json({ message: '用户名不存在' });
    10. }
    11. // 校验密码
    12. //true或false
    13. const isPasswordValid = bcrypt.compareSync(password, user.password);
    14. if (!isPasswordValid) {
    15. return res.status(422).json({ message: '密码无效' });
    16. }
    17. // 生成 JWT
    18. const token = jwt.sign({ id: String(user._id) }, SECRET); //密钥
    19. res.json({ //发送json数据
    20. user,
    21. token
    22. });
    23. } catch (error) {
    24. console.log(error)
    25. }
    26. })
    3-3.鉴权登录

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

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

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

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

    四.Vue前端

    1.封装axios

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

    1-1.http.js
    1. import axios from "axios";
    2. const http = axios.create({
    3. baseURL: 'http://127.0.0.1:3000', // 注意这里的双斜杠
    4. timeout: 5000
    5. });
    6. // 请求拦截器
    7. http.interceptors.request.use(config => {
    8. return config;
    9. }, error => {
    10. return Promise.reject(error);
    11. });
    12. // 响应拦截器
    13. http.interceptors.response.use(response => {
    14. return response.data;
    15. }, error => {
    16. return Promise.reject(error);
    17. });
    18. export default http;
    1-2.api.js
    1. import http from './http';
    2. //注册
    3. export async function registerAPI(info) {
    4. const response = await http({
    5. url: 'register',
    6. method:'post',
    7. params: {info}
    8. });
    9. return response; // 返回响应数据
    10. }
    11. //登录
    12. export async function loginAPI(info) {
    13. const response = await http({
    14. url: 'login',
    15. method:'post',
    16. params: {info}
    17. });
    18. return response; // 返回响应数据
    19. }
    20. //鉴权登录
    21. export async function getTokenAPI(config) {
    22. const response = await http({
    23. url: 'profile',
    24. method:'get',
    25. // 自定义请求头
    26. headers:{authorization:config.authorization}
    27. });
    28. return response; // 返回响应数据
    29. }
    30. //邮箱

    2.请求

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

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

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

    2-1.发送邮件

    ...

    2-2.注册
    1. import {registerAPI} from './api'
    2. ...
    3. // 注册
    4. function signUp(){
    5. //去除空格
    6. ...
    7. //判断是否为空
    8. ...
    9. let registerInfo={
    10. username:username.value,
    11. password:password.value,
    12. email:email.value,
    13. code:code.value
    14. }
    15. registerAPI(registerInfo)
    16. .then(response => {
    17. console.log('Register successful:', response);
    18. })
    19. .catch(error => {
    20. // 处理错误
    21. console.log(error)
    22. });
    23. }
    2-3.登录

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

    1. import {loginAPI} from './api'
    2. import useCookie from './Cookie';
    3. const { setCookie } = useCookie();
    4. ...
    5. //登录
    6. function SignIn(){
    7. //去除空格
    8. username.value=username.value.replace(/\s/g, '')
    9. password.value=password.value.replace(/\s/g, '')
    10. //判断是否为空
    11. if(username.value==''||password.value==''){
    12. console.log('格式不正确')
    13. }else{
    14. const loginInfo={username:username.value,password:password.value}
    15. loginAPI(loginInfo)
    16. .then(response => {
    17. setCookie('token', response.token, 0.5); // 存储30分钟
    18. console.log(response)
    19. })
    20. .catch(error => {
    21. // 处理错误
    22. console.log(error)
    23. });
    24. }
    25. }

     

    2-4.Cookie封装        
    1. export default function useCookie() {
    2. //获取Cookie
    3. const getCookie = (name) => {
    4. const value = `; ${document.cookie}`;
    5. const parts = value.split(`; ${name}=`);
    6. if (parts.length === 2) {
    7. return parts.pop().split(';').shift();
    8. }
    9. };
    10. //设置Cookie
    11. const setCookie = (name, value, hours) => {
    12. const date = new Date();
    13. date.setTime(date.getTime() + (hours * 60 * 60 * 1000));
    14. const expires = `expires=${date.toUTCString()}`;
    15. document.cookie = `${name}=${value}; ${expires}; path=/`;
    16. };
    17. //销毁Cookie
    18. const deleteCookie=(name)=>{
    19. // 将Cookie的过期时间设置为过去的日期
    20. document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
    21. }
    22. return {
    23. getCookie,
    24. setCookie,
    25. deleteCookie
    26. };
    27. }

    2-5.鉴权登录
    1. import {getTokenAPI} from './api'
    2. import useCookie from './Cookie';
    3. const { getCookie } = useCookie();
    4. ...
    5. onMounted( async()=>{
    6. // 读取Token
    7. const Token = getCookie('token');
    8. const config = {
    9. authorization: `Bearer ${Token}`, // 设置Bearer Token
    10. };
    11. // 发送需要Token的请求
    12. await getTokenAPI(config)
    13. .then(response => {
    14. // 处理响应数据
    15. console.log(response)
    16. })
    17. .catch(error => {
    18. // 处理错误
    19. console.log(error)
    20. });
    21. })

    至此前端页面完成.

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

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

  • 相关阅读:
    python学习-7-异常 错误 调试 测试
    centos7.6升级openssh9.3p1,openssl1.1.1t
    java-php-python-智能道路交通管理系统计算机毕业设计
    ITSS信息技术服务运行维护标准符合性证书申请详解及流程
    linux修改打开文件数限制open file limit
    1.6.C++项目:仿muduo库实现并发服务器之channel模块的设计
    Lite-Mono(CVPR2023)论文解读
    R语言使用lubridate包处理日期和时间数据实战
    小迪安全笔记(3)——基础入门3、基础入门4
    react中使用腾讯地图
  • 原文地址:https://blog.csdn.net/m0_71469120/article/details/132707591