本文已参与「新人创作礼」活动,一起开启掘金创作之路。
逻辑
- 验证为邮箱格式
- 发起请求 后端验证这个邮箱没有被其他账号绑定
- 生成验证码 发送到邮箱 同时 用验证码作为密钥加密用户id或者邮箱等唯一辨识信息和当前的时间 将这个token返回前端
- 前端等用户输入验证码 将token和验证码 返回后端
- 后端用验证码解密token 若成功 比对当前时间和token中的创建时间
- 时间在规定时间内则验证成功
一,用户向后端发送请求后端。对用户的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 };