Skip to content

实现一个邮箱辅助登录功能

痛点

如果有自己折腾一些自己的网站,或者做毕设的同学,相信你们一定都遇到过这么一个问题:注册时,想用微信号、手机号、邮箱来辅助登录,但是不知道怎么接入到我们的系统中,亦或是要收费。

  • 接入微信开放平台网页登录收费。
  • 手机号短信服务收费。
  • 邮件服务,我们即可自己实现。

我前后做了好几个小工具或者网站,这个问题技术问题一直没有被突破,近期终于实现了邮箱验证码系统。并接入了 DODD 中。

效果

效果如下:

image-20231212110242628

对应的邮箱会收到这么一条信息:

image-20231212110424010

image-20231212110500300

将此验证码输入之后,便可登录系统,获取对应的用户信息。

实现思路

下面我们来说说实现思路,整体上我们是需要解决这么两个问题:

  • 通过代码的方式发送邮件。
  • 将验证码&发送用户与系统进行关联,进行一些校验的逻辑。

发送邮件

之前 B 站上传过一个视频,讲了如何发送邮件,附上传送门 🚪:nodejs 实现邮件服务

核心就是通过 nodemailer 这个库,去帮助我们发送一些邮件。

这个库的核心也是去调用一些邮箱的公共接口,所以我们是需要获取自己账号的 token,并且开放一些高级权限的。

具体的如何操作不在这篇内容做详细记录,大家自行过一遍视频即可。

与现有系统进行关联

作为一名前端程序员 👨🏻‍💻,所以这一块的实现可能会有并不是非常合理的地方,还望大家可以提出更好的方案。

这里我们需要借助redis,之所以使用redis而不用mysql是因为它拥有更快的速度。

所以我们需要维护这么一个数据结构。key 为:snow-server-mail-verification-code-邮箱地址,值为随机六位数验证码。同时设置过期时间为 10 分钟。

设置了 10 分钟后,redis 会自动为我们清空掉这个 key

ts
@Post('send_verification_code')
async send(@Body() body: SendMailDto) {
  const { mail } = body;
  if (!isQQMail(mail)) {
    return { code: 500, result: '邮箱格式不正确 - 不是qq邮箱' };
  }
  const redis = await RedisInstance.initRedis();
  const code = generateRandomCode();
  const key = `snow-server-mail-verification-code-${mail}`;
  redis.set(key, code);
  redis.expire(key, 600); // 单位是秒
  await this.nodemailerService.sendVerificationCode({ to: mail, code });
  return { code: 200, result: '发送成功' };
}
@Post('send_verification_code')
async send(@Body() body: SendMailDto) {
  const { mail } = body;
  if (!isQQMail(mail)) {
    return { code: 500, result: '邮箱格式不正确 - 不是qq邮箱' };
  }
  const redis = await RedisInstance.initRedis();
  const code = generateRandomCode();
  const key = `snow-server-mail-verification-code-${mail}`;
  redis.set(key, code);
  redis.expire(key, 600); // 单位是秒
  await this.nodemailerService.sendVerificationCode({ to: mail, code });
  return { code: 200, result: '发送成功' };
}

设置上了这么个 key 之后,我们就可以在登录的时候拿上这个验证码和 key 校验一下redis中这个 key 是否一致,如果一致,则校验通过,给于登录的 token 之类的权限。

部分代码如下:

ts
@Post('login_by_mail')
async loginByMail(@Body() body: LoginByMailDto) {
  const { code, mail } = body;
  if (!isQQMail(mail)) {
    return { code: 500, result: '邮箱格式验证异常,请校验' };
  }
  let user = await this.usersService.findUserByMail(mail);
  if (!user) {
    return {
      code: 10000,
      result: '该邮箱未创建用户,请检查地址,或为该邮箱注册一个用户',
    };
  }
  const redis = await RedisInstance.initRedis();
  const key = `snow-server-mail-verification-code-${mail}`;
  const redisCode = await redis.get(key);
  if (redisCode !== code.toUpperCase()) {
    return {
      code: 500,
      result: '验证码校验失败,请重新发送验证码进行校验',
    };
  }
  if (redisCode) {
    await redis.del(key);
  }
  return await this.usersService.createToken(user);
}
@Post('login_by_mail')
async loginByMail(@Body() body: LoginByMailDto) {
  const { code, mail } = body;
  if (!isQQMail(mail)) {
    return { code: 500, result: '邮箱格式验证异常,请校验' };
  }
  let user = await this.usersService.findUserByMail(mail);
  if (!user) {
    return {
      code: 10000,
      result: '该邮箱未创建用户,请检查地址,或为该邮箱注册一个用户',
    };
  }
  const redis = await RedisInstance.initRedis();
  const key = `snow-server-mail-verification-code-${mail}`;
  const redisCode = await redis.get(key);
  if (redisCode !== code.toUpperCase()) {
    return {
      code: 500,
      result: '验证码校验失败,请重新发送验证码进行校验',
    };
  }
  if (redisCode) {
    await redis.del(key);
  }
  return await this.usersService.createToken(user);
}

总结

基于以上的两个关键步骤,我们就能够在自己的系统中接入邮箱辅助登录的服务了。对于想要自己动手做一个自己网站之类的东西还是挺不错的。

感谢观看。有不清晰的地方欢迎私信。

地址&连接

DODD - 极简的 TodoList 工具:https://tdl.jimmyxuexue.top

nodemailer - node 的邮件服务库:https://www.npmjs.com/package/nodemailer