• 4-egg-TS-通用后端管理注册系统-邮箱验证码


    1.下载Nodemailer

    npm install nodemailer
    
    • 1

    2.配置邮箱发送者
    config.local.ts开发阶段
    相关属性获取可见QQ邮箱配置

     // 邮箱相关的配置
      config.smtp = {
        host: 'smtp.qq.com',
        port: 465, // true for 465, false for other ports
        user: '1361533456@qq.com', // 发送者的邮箱
        pass: 'ajusgysoisjlpo77', // 邮箱对应的授权码
      };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.在app/util目录下新建emailCode.ts

    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const nodemailer = require('nodemailer');
    let transporter;
    export default {
      // 创建发送者对象
      createTransporterInstance(ctx) {
        if (transporter) {
          return transporter;
        }
        // 已经将相关配置写在config目录下了,比较安全
        transporter = transporter = nodemailer.createTransport({
          host: ctx.app.config.smtp.host,
          port: ctx.app.config.smtp.port,
          secure: true, // true for 465, false for other ports
          auth: {
            user: ctx.app.config.smtp.user, // 发送者的邮箱
            pass: ctx.app.config.smtp.pass, // 邮箱对应的授权码
          },
        });
      },
    
      // 创建发送内容
      createEmailInfo(ctx, receiver:string) {
        // 1.生成验证码
        const code = Math.random().toString().slice(2, 6)
          .toUpperCase();
        // 2.生成发送的内容
        const info = {
          from: '1361533033@qq.com', // 发送者
          to: receiver, // 接受者
          subject: '小灰灰管理后台注册验证码', // 邮件标题
          text: `你正在注册小灰灰管理后台系统,你的验证码是:${code}`, // 邮件内容
        };
        // 3.保存验证码
        ctx.session.email = {
          code,
          expire: Date.now() + 60 * 1000, // 一分钟后过期
        };
        return info;
      },
    
      // 异步:async 发送邮箱
      async sendEmailCode(ctx, receiver:string) {
        const transporter = this.createTransporterInstance(ctx);
        const info = this.createEmailInfo(ctx, receiver);
        return new Promise((resolve, reject) => {
          transporter.sendMail(info, (err, data) => {
            if (err) {
              reject(err);
            } else {
              resolve(data);
            }
          });
        });
      },
    
    
      verifyEmailCode(ctx, clientCode) {
        // 1.取出服务端中保存的验证码和过期时间
        const serverCaptcha = ctx.session.email;
        console.log(serverCaptcha);
        let serverCode;
        let serverExpire;
        try {
          serverCode = serverCaptcha.code;
          serverExpire = serverCaptcha.expire;
        } catch (e) {
          // 验证码无论验证成功还是失败,都只能使用一次 提高安全性
          ctx.session.email = null;
          throw new Error('请重新获取验证码1');
        }
    
    
        // 2.获得客户端传递过来的验证码
        if (Date.now() > serverExpire) {
          // 验证码无论验证成功还是失败,都只能使用一次 提高安全性
          ctx.session.email = null;
          throw new Error('验证码已经过期');
        } else if (serverCode !== clientCode) {
          // 验证码无论验证成功还是失败,都只能使用一次 提高安全性
          ctx.session.email = null;
          throw new Error('验证码不正确');
        }
        // 验证码无论验证成功还是失败,都只能使用一次 提高安全性
        ctx.session.email = null;
      },
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    4.extend/helper.ts

    import EmailCode from '../util/emailCode';
    module.exports = {
      // 异步发送邮箱
      async sendEmailCode(receiver:string) {
        return await EmailCode.sendEmailCode(this.ctx, receiver);
      },
      // 验证邮箱验证码
      verifyEmailCode(clientCode) {
        EmailCode.verifyEmailCode(this.ctx, clientCode);
      },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    5.添加发送邮件路由
    app/router.ts

    router.get('/emailcode', controller.util.emailCode);
    
    • 1

    6.发送邮箱验证码
    app/controller/util.ts

      public async emailCode() {
        const { ctx } = this;
        try {
          const { email } = ctx.query;
          const data = await ctx.helper.sendEmailCode(email);
          ctx.success(data);
        } catch (e:any) {
          ctx.error(400, e.message);
        }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    7.验证邮箱验证码
    app/controller/user.ts

     case RegisterTypeEnum.Email:
            // 校验邮箱数据的格式是否正确
            ctx.validate(EmailUserRule, data);
            // 校验邮箱验证码是否正确
            ctx.helper.verifyEmailCode(data.captcha);
            break;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    WEB 渗透之文件类操作
    私有化轻量级持续集成部署方案--06-私有镜像仓库-Harbor
    【牛客网】OR63 删除公共字符串
    面试官:了解HashSet吗?请做下面的题目
    treeSelect树组件设置父节点禁用
    iOS如何实现语音转文字功能?
    0-1背包
    ggrcs包3.5版本发布—增加了大家喜闻乐见的P for overall和主题色彩定制
    【软考:系统集成项目管理】之 五组十域图
    python+vue.js+node学生论坛系统
  • 原文地址:https://blog.csdn.net/lihui61357457/article/details/126160815