node 服务端邮箱/短信验证码,验证逻辑实现

526 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

逻辑

  1. 验证为邮箱格式
  2. 发起请求 后端验证这个邮箱没有被其他账号绑定
  3. 生成验证码 发送到邮箱 同时 用验证码作为密钥加密用户id或者邮箱等唯一辨识信息和当前的时间 将这个token返回前端
  4. 前端等用户输入验证码 将token和验证码 返回后端
  5. 后端用验证码解密token 若成功 比对当前时间和token中的创建时间
  6. 时间在规定时间内则验证成功

一,用户向后端发送请求后端。对用户的ID和随机生成六位数 也就是验证码,对用户的ID进行加密,返回前端加密后的信息。 二,前端接收到加密后的信息,当用户收到短信验证码后,填入,向后端发起请求验证验证码 是否可以解密这段加密信息。 获得用户的ID并进行比较。 通过则证明用户收到的验证码,可以解开这段加密的信息则验证通过。

代码示例

  "dependencies": {
    "nodemailer": "4.7",
  }
"use strict";
const nodemailer = require("nodemailer");
const jwt = require('jsonwebtoken');

let transporter = null;
const timeOut = 60 * 1000 * 5; // 5分钟
async function createMailServer() {
    transporter = nodemailer.createTransport({
        host: "smtp.qq.com",
        secure: true, // true for 465, false for other ports
        auth: {
            user: "zzz.qq.com", // generated ethereal user
            pass: "xxx", // generated ethereal password
        }
    });
    transporter.verify((err) => {
        if (err) console.log("邮件服务连接失败", err)
        else console.log("邮件服务连接成功👏")
    })
}

const sendMailCode = ({ findData }) => (req, res) => {
    if (!transporter) return res.status(500).json({ message: "系统出现问题" });
    let data = '';
    req.on('data', (chunk) => data += chunk);
    req.on('end', async () => {
        const { mail } = JSON.parse(data);
        //确定该邮箱没被绑定
        const [bindThisMail] = await findData({ collection: 'userProfile', find: { mail } });
        console.log("确定改邮箱没被绑定:", bindThisMail);
        if (bindThisMail) return res.status(500).json({ message: "该邮箱已被绑定" });
        const code = Array.from(new Array(5), () => Math.floor(Math.random() * 9)).join('')

        let info = await transporter.sendMail({
            from: '"hongbin" <2218176087@qq.com>', // sender address
            to: mail, // list of receivers  hellohongbin123@163.com
            subject: "验证码(五分钟有效)", // Subject line
            text: code, // plain text body
            html: code, // html body
        });
        if (info.messageId) {
            //发送邮件成功 返回 状态码加密的邮箱号
            //把当前时间一起存入
            const payload = { mail, createAt: Date.now() };
            const encryptedInfo = jwt.sign(payload, code);
            console.log("encryptedInfo:", encryptedInfo);
            res.json(encryptedInfo);
        } else res.status(500).json({ message: "发送失败" });
    });
}

const verificationMailCode = ({ updateOne, encrypt }) => (req, res) => {
    if (!transporter) return res.status(500).json({ message: "系统出现问题" });
    let data = '';
    req.on('data', (chunk) => data += chunk);
    req.on('end', async () => {
        const { code, encryptedInfo } = JSON.parse(data);
        let info;
        //解密
        try {
            info = jwt.verify(encryptedInfo, code)
        } catch (err) {
            console.log("解析 验证码和加密信息 err:", err);
            res.status(500).json({ message: '验证码错误' });
        }
        //成功 
        if (info && info.mail) {
            const { mail, createAt } = info;
            //比较时间是否超时 5 分钟
            if (Date.now() - createAt > timeOut) return res.status(500).json({ message: '验证码超时' });
            const { username, role, _id, lastUpdate } = req.userInfo;
            const updateItem = await updateOne({ collection: 'userProfile', _id, data: { mail } })
            console.log("updateItem:", updateItem);
            const user = { _id, username, role, mail, lastUpdate };
            console.log("new user:", user);
            const jwt = encrypt(user);
            res.json({ message: '验证成功', jwt, newUser: user });
        } else res.status(500).json({ message: '验证失败' });
    })
}

module.exports = { sendMailCode, createMailServer, verificationMailCode };