当前位置: 首页 > 工具软件 > Nodemailer > 使用案例 >

使用Nodemailer向邮箱用户发送验证码

太叔高义
2023-12-01


前言

在写用邮箱注册的接口,需要通过邮箱发送验证码给用户。这里记录如何使用Nodemailer给邮箱用户发送验证码。


一、Nodemailer是什么?

Nodemailer是一个用于Node.js应用程序的模块,可以方便地发送邮件。该项目开始于2010年,当时还没有发送电子邮件消息的明智选择,今天它是大多数Node.js用户默认使用的解决方案。

二、使用步骤

1.安装依赖包

npm i nodemailer

2.引入

代码如下(示例):

// controller/util.ts
const nodemailer = require("nodemailer");

3.开启POP3/SMTP服务

到邮箱设置中寻找到”开启POP3/SMTP服务“,这里我用的是qq邮箱,在设置->账户->POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务,找到后点击开启

3.创建发送者对象

不同的邮箱host的配置也不一样,qq邮箱可参考:常用邮件客户端软件设置

   let transporter = nodemailer.createTransport({
     host: "smtp.qq.com",
     port: 465,
     secure: true, // true for 465, false for other ports
     auth: {
       user: "...@qq.com", // 发送者邮箱
       pass: "...", // 邮箱对应的服务授权码
     },
   });

4.创建需要发送的内容

  let info = {
    from: "386086310@qq.com", // 谁发
    to: "386086310@qq.com", // 发给谁
    subject: "请查收:后台管理系统注册验证码", // 邮件标题
    text: `您正在注册我们的后台管理系统,您的验证码是:${code}`, // 邮件内容
    // html: "<b>Hello world?</b>", // 以 html 形式编写邮件内容
  };

5.四位数随机验证码生成

let code = Math.random().toString(16).slice(2, 6).toUpperCase();

6.发送邮件

   transporter.sendMail(info, (err, data) => {
     if (err) {
       console.log(`发送邮件失败:${err}`);
     } else {
       console.log(`发送邮件成功:${data}`);
     }
   });

附上代码

本文简单介绍了Nodemailer的使用,这里附上封装好的代码。

1.封装创建、检验验证码的功能

// util/emailCode.ts
const nodemailer = require("nodemailer");
let transporter;

export default {
  // 创建发送邮件对象
  createTransporterInstance(ctx) {
    if (transporter) {
      return transporter;
    }
    transporter = nodemailer.createTransport({
      host: ctx.app.config.smtp.host,
      port: ctx.app.config.smtp.port,
      secure: ctx.app.config.smtp.secure, // true for 465, false for other ports
      auth: {
        user: ctx.app.config.smtp.user, // 发送者邮箱
        pass: ctx.app.config.smtp.pass, // 邮箱对应的服务授权码
      },
    });
    return transporter;
  },

  // 创建发送的内容
  createEmailInfo(ctx, to: string) {
    let code = Math.random().toString(16).slice(2, 6).toUpperCase();
    let info = {
      from: ctx.app.config.smtp.user, // 谁发
      to: to, // 发给谁
      subject: "请查收:后台管理系统注册验证码", // 邮件标题
      text: `您正在注册我们的后台管理系统,您的验证码是:${code}`, // 邮件内容
      // html: "<b>Hello world?</b>", // 以 html 形式编写邮件内容
    };

    ctx.session.email = {
      code: code,
      expire: Date.now() + 60 * 1000, // 1min之后过期
    };
    console.log(code);
    return info;
  },

  // 发送验证码
  async sendEmailCode(ctx, to: string) {
    const transporter = this.createTransporterInstance(ctx);
    const info = this.createEmailInfo(ctx, to);
    return new Promise((resolve, reject) => {
      transporter.sendMail(info, (err, data) => {
        if (err) {
          reject(err);
        } else {
          resolve(data);
        }
      });
    });
  },

  verifyEmailCode(ctx, clientCode) {
    // 1.取出服务端中保存的验证码和过期时间
    const serverEmail = ctx.session.email;
    console.log(serverEmail);

    let serverCode;
    let serverExpire;
    // 无论成功还是失败验证码只能用一次 需要清空验证码缓存
    try {
      serverCode = serverEmail.code;
      serverExpire = serverEmail.expire;
    } catch (error) {
      ctx.session.email = null;
      throw new Error("请重新获取验证码");
    }

    console.log(serverCode, serverExpire, clientCode);
    if (Date.now() > serverExpire) {
      ctx.session.email = null;
      throw new Error("验证码过期");
    } else if (serverCode !== clientCode) {
      // 与客户端传递过来的验证码进行比对
      ctx.session.email = null;
      throw new Error("验证码不正确");
    }
  },
};

2.抽离

将逻辑代码抽离在 helper.ts 里面成为一个独立的函数,可以避免逻辑散乱。

import EmailCode from "../util/emailCode";

// this.ctx
module.exports = {
  async sendEmailCode(to: string) {
    return await EmailCode.sendEmailCode(this.ctx, to);
  },
  verifyEmailCode(clientCode) {
    EmailCode.verifyEmailCode(this.ctx, clientCode);
  },
};

3.编写 Controller

import { Controller } from "egg";

export default class UtilController extends Controller {
  public async emailCode() {
    const { ctx } = this;
    try {
      const { email } = ctx.query;
      const data = await ctx.helper.sendEmailCode(email);
      ctx.success(data);
    } catch (e) {
      ctx.error(400, e.message);
    }
  }
}

4.根据用户类型执行不同的函数

    switch (registerType) {
			// ...
      case RegisterTypeEnum.Email:
        ctx.validate(EmailUserRule, data);
        ctx.helper.verifyEmailCode(data.captcha);
        break;
		  // ...
      default:
        throw new Error("注册类型不存在!");
    }
 类似资料: