1.下载Nodemailer
npm install nodemailer
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', // 邮箱对应的授权码
};
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;
},
};
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);
},
};
5.添加发送邮件路由
app/router.ts
router.get('/emailcode', controller.util.emailCode);
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);
}
}
7.验证邮箱验证码
app/controller/user.ts
case RegisterTypeEnum.Email:
// 校验邮箱数据的格式是否正确
ctx.validate(EmailUserRule, data);
// 校验邮箱验证码是否正确
ctx.helper.verifyEmailCode(data.captcha);
break;