You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用JS发送邮件?购物网站邮箱注册验证码功能求助

购物网站邮箱注册+验证码功能实现方案

核心流程拆解

  • 用户输入邮箱,前端完成格式校验
  • 后端生成6位随机验证码,将验证码与邮箱绑定并设置过期时间(建议5分钟)
  • 后端调用邮件服务商API发送验证码邮件
  • 用户输入验证码后,前端提交至后端校验有效性
  • 校验通过后完成用户注册,同时清理已使用的验证码

后端实现示例(Node.js)

以Node.js + Express + Redis + Nodemailer为例,先安装依赖:

npm install express nodemailer redis bcrypt

核心代码

const express = require('express');
const nodemailer = require('nodemailer');
const redis = require('redis');
const bcrypt = require('bcrypt');
const app = express();
app.use(express.json());

// 初始化Redis客户端(本地服务示例,生产环境需配置对应参数)
const redisClient = redis.createClient();
redisClient.connect().catch(console.error);

// 配置邮件传输器(以QQ邮箱为例,其他邮箱替换对应配置)
const mailTransporter = nodemailer.createTransport({
  service: 'qq',
  auth: {
    user: 'your-business-email@qq.com',
    pass: 'your-email-authorization-code' // 邮箱授权码,非登录密码
  }
});

// 生成6位随机验证码
const generateVerificationCode = () => {
  return Math.floor(100000 + Math.random() * 900000).toString();
};

// 发送验证码接口
app.post('/api/send-code', async (req, res) => {
  const { email } = req.body;
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  
  // 邮箱格式校验
  if (!emailRegex.test(email)) {
    return res.status(400).json({ msg: '邮箱格式不正确' });
  }

  // 限制发送频率(1分钟内只能发一次)
  const lastSendTime = await redisClient.get(`send_time:${email}`);
  if (lastSendTime && Date.now() - parseInt(lastSendTime) < 60000) {
    return res.status(400).json({ msg: '请1分钟后再尝试发送' });
  }

  const code = generateVerificationCode();
  // 存储验证码,5分钟过期
  await redisClient.setEx(`code:${email}`, 300, code);
  // 记录发送时间
  await redisClient.setEx(`send_time:${email}`, 60, Date.now().toString());

  // 发送邮件
  const mailOptions = {
    from: '你的购物网站 <your-business-email@qq.com>',
    to: email,
    subject: '你的购物网站注册验证码',
    html: `<p>您好!您的注册验证码是:<strong>${code}</strong></p><p>验证码5分钟内有效,请及时完成注册。</p>`
  };

  mailTransporter.sendMail(mailOptions, (err, info) => {
    if (err) {
      console.error(`发送邮件失败:${err}`);
      return res.status(500).json({ msg: '验证码发送失败,请稍后重试' });
    }
    res.status(200).json({ msg: '验证码已发送至您的邮箱' });
  });
});

// 用户注册接口
app.post('/api/register', async (req, res) => {
  const { email, code, password } = req.body;

  // 校验验证码
  const storedCode = await redisClient.get(`code:${email}`);
  if (!storedCode || storedCode !== code) {
    return res.status(400).json({ msg: '验证码无效或已过期' });
  }

  // 密码加密(10为哈希加盐轮数,越高越安全但耗时更长)
  const hashedPassword = await bcrypt.hash(password, 10);

  // 此处添加将用户信息(email、hashedPassword)存入数据库的逻辑
  // 示例(MongoDB + Mongoose):
  // const newUser = new User({ email, password: hashedPassword });
  // await newUser.save();

  // 清理验证码和发送时间记录
  await redisClient.del(`code:${email}`);
  await redisClient.del(`send_time:${email}`);

  res.status(200).json({ msg: '注册成功' });
});

app.listen(3000, () => console.log('服务启动在3000端口'));

前端交互实现

HTML表单

<div class="register-container">
  <form id="registerForm">
    <div class="form-group">
      <label>邮箱</label>
      <input type="email" id="emailInput" placeholder="请输入您的邮箱" required>
    </div>
    <div class="form-group">
      <label>验证码</label>
      <div class="code-input-group">
        <input type="text" id="codeInput" placeholder="请输入验证码" maxlength="6" required>
        <button type="button" id="sendCodeBtn">发送验证码</button>
      </div>
    </div>
    <div class="form-group">
      <label>密码</label>
      <input type="password" id="passwordInput" placeholder="请设置密码(不少于6位)" minlength="6" required>
    </div>
    <button type="submit" class="submit-btn">完成注册</button>
  </form>
</div>

JavaScript逻辑

const sendCodeBtn = document.getElementById('sendCodeBtn');
const registerForm = document.getElementById('registerForm');
let countdownTimer = null;

// 发送验证码按钮点击事件
sendCodeBtn.addEventListener('click', async () => {
  const email = document.getElementById('emailInput').value;
  if (!email) {
    alert('请先输入邮箱');
    return;
  }

  // 按钮倒计时逻辑
  sendCodeBtn.disabled = true;
  let countdown = 60;
  sendCodeBtn.textContent = `${countdown}秒后重发`;
  countdownTimer = setInterval(() => {
    countdown--;
    sendCodeBtn.textContent = `${countdown}秒后重发`;
    if (countdown <= 0) {
      clearInterval(countdownTimer);
      sendCodeBtn.disabled = false;
      sendCodeBtn.textContent = '发送验证码';
    }
  }, 1000);

  // 调用后端接口
  try {
    const response = await fetch('/api/send-code', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email })
    });
    const data = await response.json();
    if (!response.ok) throw new Error(data.msg);
    alert(data.msg);
  } catch (err) {
    alert(err.message);
    // 出错后恢复按钮状态
    clearInterval(countdownTimer);
    sendCodeBtn.disabled = false;
    sendCodeBtn.textContent = '发送验证码';
  }
});

// 表单提交事件
registerForm.addEventListener('submit', async (e) => {
  e.preventDefault();
  const email = document.getElementById('emailInput').value;
  const code = document.getElementById('codeInput').value;
  const password = document.getElementById('passwordInput').value;

  try {
    const response = await fetch('/api/register', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, code, password })
    });
    const data = await response.json();
    if (!response.ok) throw new Error(data.msg);
    alert('注册成功!即将跳转至首页');
    // 跳转逻辑
    setTimeout(() => window.location.href = '/', 1500);
  } catch (err) {
    alert(err.message);
  }
});

关键注意事项

  • 邮箱授权码获取:登录邮箱后台开启POP3/SMTP服务,获取授权码替代登录密码,避免账号泄露风险。
  • 验证码安全:禁止将验证码明文存储在数据库,用Redis的过期键自动清理无效验证码。
  • 接口防护:添加频率限制(如1分钟内同一邮箱只能发一次验证码),防止恶意刷接口消耗邮件资源。
  • 密码安全:必须使用bcrypt等哈希算法加密用户密码,绝对禁止明文存储。
  • 邮件内容优化:避免使用敏感词汇,添加网站品牌标识,降低被判定为垃圾邮件的概率。
  • 异常处理:前后端都要添加异常捕获逻辑,给用户明确的错误提示,提升体验。

内容的提问来源于stack exchange,提问作者levan tulashvili

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 01:59:54