如何实现邮箱+验证码登录功能(express+vue+MySQL版)

目录

  • 1. 初始化项目
  • 2. 配置环境变量
  • 3. 更新数据库
  • 4. 编写路由函数
  • 5. 前端调用接口

1. 初始化项目

前端根目录:/web
后端根目录:/api_server
安装依赖:

npm install express mysql nodemailer randomstring dotenv

其中,nodemailer是用来发送验证码的node.js依赖库, randomstring是用于生成随机字符串的 Node.js 库(即在这里生成验证码),dotenv用于在.env文件中配置敏感信息,避免直接暴露在代码中

2. 配置环境变量

在根目录/api_server下创建/.env文件

EMAIL_SERVICE=smtp.126.com  // 使用网易邮箱发送验证码
EMAIL_PORT=465  # 使用SSL的端口
EMAIL_SECURE=true  # 使用SSL
[email protected]  // 用你的邮箱码代替
EMAIL_PASS=YOURPASSWORD   // 客户端授权密码
DB_HOST=localhost
DB_USER=YOURUSERNAME  // 用你的数据库用户名代替
DB_PASSWORD=YOURUSERPASSWORD  // 用你的数据库密码代替
DB_NAME=YOURDBNAME  // 用你的数据库名代替

在后端入口文件中导入

require('dotenv').config()

3. 更新数据库

在你的项目数据库中添加列:email(存放邮箱号)、 code(存放验证码)、expires_at(存放验证码过期时间)

4. 编写路由函数

/api_server/router/user.js

// 登录路由
router.post('/login', userHandler.login)

// 发送验证码路由
router.post('/send-verification-code', userHandler.sendVerificationCode)

/api_server/router_handler/user.js

// 创建可重用的 transporter 对象用于发送邮件
const transporter = nodemailer.createTransport({
  host: 'smtp.126.com', // SMTP服务器地址
  port: 465, // 端口号
  secure: true, // 使用SSL
  auth: {
    user: process.env.EMAIL_USER,
    pass: process.env.EMAIL_PASS
  }
})

// 辅助函数:生成并存储验证码
const generateAndStoreVerificationCode = async (email) => {
  // 生成验证码
  const code = randomstring.generate({
    length: 6,  //验证码长度
    charset: 'numeric'  //可以选择 'alphabetic', 'numeric', 'hexadecimal', 'binary' 或者自定义字符集
  })
  // 存储验证码
  // const createdAt = new Date()
  const expiresAt = new Date(Date.now() + 5 * 60 * 1000) // 验证码有效期为 5 分钟
  try {
    const sqlStr = 'UPDATE user SET code=?, expires_at=? WHERE email=?'
    await db.query(sqlStr, [code, expiresAt, email])
    return { code, expiresAt }
  } catch (error) {
    console.error('存储验证码失败:', error)
    throw error
  }
}

const util = require('util')
const query = util.promisify(db.query).bind(db)
// 辅助函数:验证验证码
const verifyCode = async (email, inputCode) => {
  try {
    // 定义查找该用户验证码的sql语句
    const sqlStr = 'select * from user where email=? and expires_at > NOW()'
    // 执行sql语句
    const results = await query(sqlStr, [email])
    if (results.length === 0) {
      return false
    }
    // 拿到用户数据库中存储的验证码
    const storeCode = results[0].code
    // 比较验证
    if (storeCode === inputCode) {
      // 验证成功后删除验证码
      const sqlStr_delete_code = 'UPDATE user SET code = null WHERE email=?'
      db.query(sqlStr_delete_code, [email])
      return true
    }
    return false
  } catch (error) {
    console.error('验证验证码失败:', error)
    throw error
  }
}

// 邮箱登录时发送验证码的处理函数
exports.sendVerificationCode = async (req, res) => {
  // 接收邮箱号
  const { email } = req.body
  if (!email) {
    return res.send({ status: 1, message: '请提供邮箱地址' })
  }
  try {
    const { code, expiresAt } = await generateAndStoreVerificationCode(email)
    // 构建邮件内容
    const mailOptions = {
      from: process.env.EMAIL_USER,
      to: email,
      subject: `您的验证码 ${code}`,
      text: `您好,您的验证码是:${code}。请不要告诉他人。验证码将在 ${expiresAt.toISOString()} 前有效。`,
      html: `你的验证码是 ${code},请勿告诉他人。` // HTML 内容
    
    // 发送邮件
    await transporter.sendMail(mailOptions)
    res.send({status: 0, message: '验证码已发送,请查收邮件' })
  } catch (error) {
    res.send({status: 1, message: '发送验证码时出错' })
  }
}

// 用户登录的处理函数
exports.login = (req, res) => {
   // 定义邮箱密码登陆方式的sql语句
   const sqlStr_email = 'select * from user where email=?'
   // 执行sql语句,根据邮箱查询用户信息
   db.query(sqlStr_email, [userInfo.email], async (err, results) => {
     // 判断是否查询成功
     if (err) {
       return res.send({ status: 1, message: err })
     }
     // 执行sql语句成功,但是获取的条数不等于1
     if (results.length === 0) {
       return res.send({ status: 1, message: '该用户不存在' })
     } else if (results.length !== 1) {
       return res.send({ status: 1, message: '登录失败' })
     }
     // TODO:判断验证码是否正确
     const isTrue = await verifyCode(userInfo.email, userInfo.code)
     // 验证码错误
     console.log('isTrue:', isTrue)      
     if (!isTrue) {
       return res.send({ status: 1, message: '验证码错误或已过期'})
     }
   })
   
	····
	
}

【注意】:验证验证码函数使用时需要先导入util 模块,将 Node.js 中传统的基于回调的 API 转换为返回 Promise 的形式,否则无法直接通过const results = await db.query(sqlStr, [email])拿到查找的返回值

5. 前端调用接口

/web/api/user.js

// 登录接口
export const useLoginService = ({ email, code }) => {
  return request.post(...', { email, code })
}

// 发送验证码
export const useSendVerificationCode = ({ email }) => {
  return request.post('...', { email })
}

/web/views/login



你可能感兴趣的:(express,vue.js,mysql)