koa+nodemailer实现邮箱验证注册功能

一、辅助代码

在server/dbs/config.js里定义redis、smtp等相关配置:

module.exports = {
  dbs: 'mongodb://127.0.0.1:27017/student',
  redis: {
    get host() {
      return '127.0.0.1'
    },
    get port() {
      return 6379
    }
  },
  smtp: {
    get host() {
      return 'smtp.qq.com'
    },
    get user() {
      return 'xxxx@qq.com'
    },
    get pass() {
      return 'xxxxxxxxxxxxx'
    },
    get code() {
      return () => {
        return Math.random().toString(16).slice(2, 6).toUpperCase()
      }
    },
    get expire() {
      return () => {
        return new Date().getTime() + 60 * 1000
      }
    }
  }
}

在server/dbs/models/users.js定义User表,用来存储用户信息:

const mongoose = require('mongoose');
//定义一个描述表结构的Schema
let userSchema = new mongoose.Schema({
  username: {
    type: String,
    unique: true,
    require: true
  },
  password: {
    type: String,
    require: true
  },
  email: {
    type: String,
    require: true
  }
})
//把这个 schema 编译成一个 Model,和表联系起来,model 是我们构造 document 的 Class
module.exports = mongoose.model('User', userSchema)

在入口文件server/index.js里做连接数据库、配置路由等工作

const Koa = require('koa')
const { Nuxt, Builder } = require('nuxt')
const mongoose = require('mongoose')
const consola = require('consola')
const config = require('../nuxt.config.js')
const dbConfig = require('./dbs/config')
const json = require('koa-json')
const bodyParser = require('koa-bodyparser')
const users = require('./interface/users')
const app = new Koa()
config.dev = app.env !== 'production'
//koa-bodyparser:处理程序之前,在中间件中对传入的请求体进行解析
app.use(bodyParser({
  extendTypes: ['json', 'form', 'text']
}))
//koa-json:让 Koa2 支持响应 JSON 数据
app.use(json())
//连接数据库
mongoose.connect(dbConfig.dbs, {
  useNewUrlParser: true
})
async function start() {
  // Instantiate nuxt.js
  const nuxt = new Nuxt(config)

  const {
    host = process.env.HOST || '127.0.0.1',
    port = process.env.PORT || 3000
  } = nuxt.options.server

  // Build in development
  if (config.dev) {
    const builder = new Builder(nuxt)
    await builder.build()
  } else {
    await nuxt.ready()
  }
  app.use(users.routes(), users.allowedMethods())
  app.use((ctx) => {
    ctx.status = 200
    ctx.respond = false // Bypass Koa's built-in response handling
    ctx.req.ctx = ctx // This might be useful later on, e.g. in nuxtServerInit or with nuxt-stash
    nuxt.render(ctx.req, ctx.res)
  })

  app.listen(port, host)
  consola.ready({
    message: `Server listening on http://${host}:${port}`,
    badge: true
  })
}

start()

二、核心代码

在server/interface/users.js里定义接口:

const Router = require('koa-router')
const Redis = require('koa-redis')
const nodemailer = require('nodemailer')
const User = require('../dbs/models/users')
const Email = require('../dbs/config')
//新建router实例
let router = new Router({
  prefix: '/users'
})
//新建redis客户端
const Store = new Redis().client
// signup 注册接口实现
router.post('/signup', async (ctx, next) => {
  const { code, username, password, email } = ctx.request.body
  //校验code
  if (code) {
    const saveCode = await Store.hget(`nodemail:${username}`, 'code')
    const saveExpire = await Store.hget(`nodemail:${username}`, 'expire')
    if (code === saveCode) {
      if (new Date().getTime() - saveExpire > 0) {
        ctx.body = {
          code: -1,
          msg: '验证码已过期,请重新尝试'
        }
        return false
      }
    } else {
      ctx.body = {
        code: -1,
        msg: '请填写正确的验证码'
      }
      return false
    }
  } else {
    ctx.body = {
      code: -1,
      msg: '请填写验证码'
    }
    return false
  }
  //校验 username是否已经存在数据库
  const name = await User.find({ username })
  if (name.length) {
    ctx.body = {
      code: -1,
      msg: '用户名已存在'
    }
    return
  }
  // 把注册信息username、password、email存数据库
  const user = await User.create({username, password, email})
  try {
    user.save()
    ctx.body = {
      code: 0,
      msg: '注册成功',
    }
    return
  } catch (e) {
    ctx.body = {
      code: -1,
      msg: '服务端错误'
    }
    return
  }
})
//verify 发送验证码接口实现
router.post('/verify', async (ctx, next) => {
  //校验是不是一分钟之内
  let username = ctx.request.body.username;
  let email = ctx.request.body.email;
  const saveExpire = await Store.hget(`nodemail:${username}`, 'expire');
  if (saveExpire && new Date().getTime() - saveExpire < 0) {
    ctx.body = {
      code: -1,
      msg: '验证请求过于频繁,1分钟内1次'
    }
    return false
  }
  //发送端信息
  let transporter = nodemailer.createTransport({
    service: 'qq', 
    port: 465, // SMTP 端口
    secureConnection: true, // 使用了 SSL
    auth: {
      user: Email.smtp.user,
      pass: Email.smtp.pass
    }
  });
  //接受端信息
  let ko = {
    code: Email.smtp.code(),
    expire: Email.smtp.expire(),
    email,
    user: username
  }
  //邮件信息
  let mailOptions = {
    from: `《李赵测试网站》<${Email.smtp.user}>`,
    to: ko.email,
    subject: '李赵测试网站验证码',
    html: `${ko.user}您好,您正在《李赵测试网站》注册,验证码是:${ko.code}`
  };
  //发送邮件
  transporter.sendMail(mailOptions, (error, info) => {
    if (error) {
      return console.log(error);
    } else {
      Store.hmset(`nodemail:${ko.user}`, 'code', ko.code)
      Store.hmset(`nodemail:${ko.user}`, 'expire', ko.expire)
      Store.hmset(`nodemail:${ko.user}`, 'email', ko.email)
    }
  });
  //ctx返回值
  ctx.body = {
    code: 0,
    msg: '验证码发送成功'
  }
})
module.exports = router
原文地址:https://www.cnblogs.com/superlizhao/p/12132123.html