Node.js使用nodemailer发送邮件,完成验证码注册

完成邮件发送验证码部分

1.开启SMTP服务(以qq邮箱为例)

(1).设置—>账号—>开启服务,如下图所示

(2).点击管理服务—>安全设置最下面—>生成授权码(代码中有效)

2.后端代码部分(后端使用的是express)

(1).先安装nodemailer(我使用的是6.7.0版本)
npm i nodemailer@6.7.0
(2).schma.js
// 邮箱的验证规则
const email = joi.string().email().required().error(new Error("请输入正确规则的邮箱账号"))
//发送邮箱的验证规则对象
exports.reg_sendcode_schema = {
  body: {
    email,
  },
}
(3).router.js
//2.导入需要的验证规则对象
const {reg_sendcode_schema} = require('../schema/user')
//发送邮箱
router.post('/email',expressJoi(reg_sendcode_schema),user_handler.email)
(4).router_handler.js
//发送验证码
exports.email = (req, res) => {
    const email = req.body.email;
    const checkEmailSql = 'SELECT * FROM users WHERE email = ?';
    db.query(checkEmailSql, [email], (err, results) => {
        if (err) {
            return res.cc(err);
        }

        if (results.length > 0) {
            return res.cc('邮箱已被注册!');
        }
        // 每次发送前清除之前保存的验证码(用户可以多次点击发送验证码)
        const clearCodeSql = "UPDATE users SET code = NULL WHERE email = '你的邮箱' ";
        db.query(clearCodeSql, (err, results) => {
            if (err) return res.cc(err);
                let transporter = nodemailer.createTransport({
                    service: 'qq', //邮箱类型
                    secure: true, //是否使用安全连接,对https协议的
                    port: 465, //qq邮件服务所占用的端口
                    auth: {
                        user: "你的邮箱", //开启SMTP的邮箱,发件人
                        pass: "你生成的授权码" //qq授权码
                    },
                });
                //产生随机验证码
                var code = "";
                for (var i = 0; i < 6; i++) {
                    code += Math.floor(Math.random() * 10);
                }
                let options = {
                    from: "你的邮箱", //发送方
                    to: req.body.email, //接收方
                    subject: "激活验证码", //邮件主题
                    text:"你的验证码:" + code  // 邮件正文
                };
                transporter.sendMail(options, (err, info) => {
                    if (err) {
                        res.send(err);
                    } else {
                        //加密验证码
                        code = bcrypt.hashSync(code, 10);
                        const sendCodeSql = "update users set code=? where email='你的邮箱'";
                        db.query(sendCodeSql, [code], (err, results) => {
                            if (err) return res.cc(err);
                            if (results.affectedRows !== 1) return res.cc("发送验证码失败!");
                            res.send({
                                status: 0,
                                message: "发送验证码成功!",
                            });
                        });
                    }
                });
            })
    })
}

3.前端代码(前端我使用的是Vue3+element-plus)

(1).邮箱的接口(我使用的是axios发送请求)
//引入axios的实例
import axios from '@/http/index.js'
//邮箱的接口
export const sendcode = data => {
    const {
        email,
    } = data
    return instance({
        url: '/api/email',
        method: 'POST',
        data: {
            email,
        }
    })
}
(2).注册页面(这里的ElMessage是element-plus内置的弹窗提示)
//html部分
<el-button class="send-code" @click="sendCode">发送验证码</el-button>
import { ElMessage } from 'element-plus'
//js部分
//注册表单数据(:formData是ts定义的接口,做类型判断)
const registerData: formData = reactive({
    username: '',
    email: '',
    password: '',
    repassword: '',
    code: null
})

const validateEmail = (value) => {
    let reg = /^[A-Za-z\d]+([-_.][A-Za-z\d]+)*@([A-Za-z\d]+[-.])+[A-Za-z\d]{2,5}$/;
    return reg.test(value)
}
//发送邮箱
const sendCode = async () => {
    const email = registerData.email;
    const res = await sendcode(registerData);
    if (validateEmail(email)) {
        //发送验证码成功
        if (res.data.status == 0) {
            //后端返回成功的值
            ElMessage.success(res.data.message);
        } else {
            //后端返回失败的值
            ElMessage.error(res.data.message);
        }
    } else {
        ElMessage.error('请输入正确的邮箱地址');
    }
}

完成注册,验证表单信息

1.验证用户全部的注册信息(下图为我的前端页面)

2.后端代码

(1).schma.js
// 用户名的验证规则
const username = joi.string().alphanum().min(3).max(10).required().error(new Error('用户名为3-10位任意字符'))
// 密码的验证规则
const password = joi.string().pattern(/^[\S]{6,12}$/).required().error(new Error("密码为6-12位任意字符"))
// 邮箱的验证规则
const email = joi.string().email().required().error(new Error("请输入正确规则的邮箱账号"))
//验证码的验证规则
const code = joi.string().regex(/^\d{6}$/).required().error(new Error("请输入六位数字的验证码"));
// 注册表单的验证规则对象
exports.reg_register_schema = {
  // 表示需要对 req.body 中的数据进行验证
  body: {
    username,
    password,
    email,
    code,
  },
}
(2).router.js
//导入需要的验证规则对象
const {reg_register_schema} = require('../schema/user')
//注册新用户
router.post('/register',expressJoi(reg_register_schema),user_handler.register)
(3).router_handler.js
//注册新用户的处理函数
exports.register = (req, res) => {
    //获取客户端提交到服务器的用户信息
    const userInfo = req.body

    //定义SQL语句,查询用户名是否被占用
    const sqlStr = 'select * from users where username=? OR email=?'
    db.query(sqlStr, [userInfo.username, userInfo.email], (err, results) => {
        //执行SQL语句失败
        if (err) {
            return res.cc(err)
        }
        //判断用户名和邮箱是否被占用
        if (results.length > 0) {
            for (let i = 0; i < results.length; i++) {
                if (results[i].username === userInfo.username) {
                    return res.cc('用户名已被占用,请更换其他用户名!');
                }
                if (results[i].email === userInfo.email) {
                    return res.cc('邮箱已被注册!');
                }
            }
        }

        //验证验证码
        const checkcodeSql = "select code from users where email='你的邮箱'";
        db.query(checkcodeSql, (err, results) => {
            if (err) {
                return res.cc(err);
            }
            if (results.length === 0) {
                return res.cc("用户查找失败!");
            }
            const code = results[0].code;
            if (!code) {
                return res.cc("未发送验证码!");
            }
            const compareResult = bcrypt.compareSync(req.body.code, results[0].code);
            if (!compareResult) {
                return res.cc("验证码错误!");
            } else {
                // 若验证码正确之后,需要将数据库中的验证码清除
                const clearcodedSql = "update users set code=null where email='你的邮箱'"
                db.query(clearcodedSql, (err, results) => {
                    console.log(results)
                    if (err) {
                        return res.cc(err);
                    }
                    if (results.affectedRows !== 1) {
                        return res.cc("验证码清除失败!");
                    }

                    //调用bcrypt.hashSync()对密码进行加密
                    userInfo.password = bcrypt.hashSync(userInfo.password, 10)

                    //定义插入新用户的sql语句
                    const sql = 'insert into users set ?'
                    //注册身份
                    const identity = '用户'
                    //创建时间
                    const create_time = new Date()
                    db.query(sql, {
                        username: userInfo.username,
                        password: userInfo.password,
                        email: userInfo.email,
                        //身份
                        identity,
                        //创建时间
                        create_time,
                        //初始没冻结
                        status: 0,
                    },
                        function (err, results) {
                            // 执行 SQL 语句失败
                            // if (err) return res.send({ status: 1, message: err.message })
                            if (err) {
                                return res.cc(err)
                            }
                            // SQL 语句执行成功,但影响行数不为 1
                            if (results.affectedRows !== 1) {
                                //   return res.send({ status: 1, message: '注册用户失败,请稍后再试!' })
                                return res.cc('注册用户失败,请稍后再试!')
                            }
                            // // 注册用户成功
                            return res.cc('注册成功!', 0)
                        })
                })
            }
        })
    })
}

3.前端代码

(1).用户注册的接口
//注册的接口
export const register = data => {
    const {
        username,
        password,
        email,
        code,
    } = data
    return instance({
        url: '/api/register',
        method: 'POST',
        data: {
            username,
            password,
            email,
            code
        }
    })
}
(2).前端页面
//html部分
<el-button type="primary" plain @click="Register">注册</el-button>
//js部分
//注册表单数据(:fromData是ts的定义的接口)
const registerData: formData = reactive({
    username: '',
    email: '',
    password: '',
    repassword: '',
    code: null
})
//注册
const Register = async () => {
    const res = await register(registerData)

    if (registerData.password == registerData.repassword) {
        if (res.data.status == 0) {
            ElMessage({
                message: res.data.message,
                type: 'success'
            })
            //切换到登录
            activeName.value = 'first'
            // 清空registerData对象数据
            for (let key in registerData) {
                registerData[key] = null;
            }
        } else {
            ElMessage.error(res.data.message)
        }
    } else {
        ElMessage.error('两次密码输入不正确')
    }
}

总结:

后端保存code的值是拿管理员的定义了一个code字段保存的,然后做清空添加的操作,虽然我做了很多判断处理,但感觉这个方法还是不行,可能存在bug,如果有更好的办法,把这个code缓存起来会更好。

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值