网站邮箱验证实现方法

137 阅读2分钟

网站邮箱验证实现方法

以下是一种基于验证链接的常见邮箱验证实现方案,使用Node.js + Express + MongoDB技术栈实现:

📧 实现原理

graph TD
    A[用户注册] -->|提交邮箱| B[生成验证Token]
    B --> C[发送验证邮件]
    C --> D[用户点击邮件链接]
    D --> E[验证Token有效性]
    E -->|有效| F[更新用户验证状态]
    E -->|无效| G[返回错误信息]

🛠️ 具体实现步骤

1. 用户模型设计(models/user.js)

const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
  email: { type: String, required: true, unique: true },
  password: { type: String, required: true },
  isVerified: { type: Boolean, default: false },
  verificationToken: { type: String },
  tokenExpires: { type: Date },
});

module.exports = mongoose.model('User', userSchema);

2. 生成并保存验证令牌(controllers/auth.js)

const crypto = require('crypto');
const User = require('../models/user');

async function register(req, res) {
  const { email, password } = req.body;
  
  // 生成唯一验证令牌
  const verificationToken = crypto.randomBytes(32).toString('hex');
  const tokenExpires = new Date(Date.now() + 24 * 3600000); // 24小时有效期
  
  const newUser = new User({
    email,
    password, // 实际应用中应加密存储
    verificationToken,
    tokenExpires
  });
  
  try {
    await newUser.save();
    
    // 发送验证邮件
    const verificationLink = `https://yourdomain.com/verify?token=${verificationToken}&email=${email}`;
    sendVerificationEmail(email, verificationLink);
    
    res.status(201).json({ message: '注册成功!请查收验证邮件' });
  } catch (error) {
    res.status(500).json({ error: '注册失败' });
  }
}

3. 发送验证邮件(utils/email.js)

const nodemailer = require('nodemailer');

const transporter = nodemailer.createTransport({
  host: 'smtp.example.com',
  port: 587,
  secure: false, // true for 465, false for other ports
  auth: {
    user: process.env.EMAIL_USER,
    pass: process.env.EMAIL_PASS,
  },
});

function sendVerificationEmail(email, verificationLink) {
  const mailOptions = {
    from: '"网站团队" <no-reply@example.com>',
    to: email,
    subject: '请验证您的邮箱',
    html: `
      <h1>感谢您的注册!</h1>
      <p>请点击以下链接验证您的邮箱,该链接24小时内有效:</p>
      <a href="${verificationLink}">验证邮箱</a>
      <p>如果您无法点击链接,请复制以下网址到浏览器:</p>
      <p>${verificationLink}</p>
    `,
  };

  transporter.sendMail(mailOptions, (error, info) => {
    if (error) {
      console.error('发送邮件失败:', error);
    } else {
      console.log('验证邮件已发送至:', email);
    }
  });
}

4. 处理验证请求(controllers/auth.js)

async function verifyEmail(req, res) {
  const { token, email } = req.query;
  
  try {
    const user = await User.findOne({ 
      email,
      verificationToken: token,
      tokenExpires: { $gt: Date.now() } // 检查是否过期
    });
    
    if (!user) {
      return res.status(400).json({ error: '验证链接无效或已过期' });
    }
    
    // 更新用户状态
    user.isVerified = true;
    user.verificationToken = undefined;
    user.tokenExpires = undefined;
    await user.save();
    
    res.status(200).json({ message: '邮箱验证成功!' });
  } catch (error) {
    res.status(500).json({ error: '服务器错误' });
  }
}

5. 路由设置(routes/auth.js)

const express = require('express');
const router = express.Router();
const authController = require('../controllers/auth');

router.post('/register', authController.register);
router.get('/verify', authController.verifyEmail);

module.exports = router;

🛡️ 安全性考虑

  1. HTTPS: 整个验证流程必须使用HTTPS协议确保安全
  2. 令牌时效: 验证链接有效期限制在24小时
  3. 一次性使用: 验证成功后立即清除验证令牌
  4. 防暴力破解: 限制IP请求频率
  5. 敏感数据: 避免在URL中暴露过多个人信息

🔄 处理验证邮件丢失的情况

// 重发验证邮件功能
async function resendVerificationEmail(req, res) {
  const { email } = req.body;
  
  try {
    const user = await User.findOne({ email });
    
    if (!user) {
      return res.status(404).json({ error: '用户不存在' });
    }
    
    if (user.isVerified) {
      return res.status(400).json({ error: '邮箱已验证' });
    }
    
    // 重新生成令牌
    user.verificationToken = crypto.randomBytes(32).toString('hex');
    user.tokenExpires = Date.now() + 24 * 3600000;
    await user.save();
    
    // 重新发送邮件
    const verificationLink = `https://yourdomain.com/verify?token=${user.verificationToken}&email=${email}`;
    sendVerificationEmail(email, verificationLink);
    
    res.status(200).json({ message: '验证邮件已重新发送' });
  } catch (error) {
    res.status(500).json({ error: '服务器错误' });
  }
}

📈 实际部署建议

  1. 将邮件服务配置信息存储在环境变量中
  2. 使用Redis等内存数据库存储验证令牌提升性能
  3. 实现邮件发送队列,避免高并发时邮件服务拥塞
  4. 添加SPF、DKIM和DMARC记录改善邮件送达率
  5. 验证成功后自动登录用户或重定向到登录页面