nodejs实现用户邮箱注册
前言
本篇博文以实战的角度描述用户注册的实现过程.整个项目使用koa2框架搭建.
用户注册的流程简述如下:用户向接口地址发送三个字段用户名user_name,密码password和邮箱email.后端接受后向数据库中的用户表插入一条新的记录,但设置该条用户记录的状态为不可用.随后向用户邮箱发送一个校验的url,用户打开邮箱里面的地址跳转之后,数据库对应的那条用户记录状态由不可用置为可用,至此完后用户注册功能.
用户数据表如下
项目快速搭建
新建项目文件夹,在命令行工具下输入npm init -y初始化项目.使用npm安装相应的依赖包,如下:
npm i @koa/router koa koa-bodyparser sequelize mysql2 --save
npm i nodemon -D
- koa/router用于构建路由.koa-bodyparser中间件可以让后端轻松接受到用户使用POST请求传递过来的参数.sequelize和mysql2是操作数据库所需要的依赖
- nodemon可以实时监听文件变化自动更新nodejs代码,用于提升开发效率
项目目录如下:
app.js是整个项目的入口文件,分别引入路由模块router和bodyParser中间件,并使用app的实例进行装载.如下:
const Koa = require('koa');
const app = new Koa();
const { router } = require('./router/user'); //引入用户路由模块
const bodyParser = require('koa-bodyparser');
app.use(bodyParser()); //解析post请求
app.use(router.routes()); //加载用户路由模块
//用户如果访问根地址,返回'Hello world'
app.use((ctx) => {
ctx.body = 'Hello world';
});
app.listen(3000);
user.js定义用户的路由模块,如下:
const Router = require('@koa/router');
const router = new Router();
/**
* 注册
*
* 用户如果访问/register,就返回'注册'
*/
router.get('/register', async (ctx) => {
ctx.body = '注册';
});
exports.router = router;
最后在pakage.json中配置启动的参数
回到项目根目录,打开命令行工具运行npm run dev就可以成功启动项目了.此时打开浏览器访问接口地址可以看到数据返回.
注册功能
将注册的接口改为post方法,通过ctx.request.body可以轻松的获取到用户发送的三个字段.路由层的使命到此为止,剩下的工作交给业务层处理.赋予ctx.body的数据将返回给用户,这就意味着业务层处理的结果直接返回给用户.
/**
* 注册
*/
router.post('/register', async (ctx) => {
const { user_name, password, email } = ctx.request.body; //在app.js中使用bodyParser中间件后,这里可以轻松获取到post请求传递过来的参数
ctx.body = await register({ user_name, password, email });
});
业务层的register函数是实现注册的核心代码(如下).注册新用户首先要判断该用户有没有注册过,如果用户名user_name和邮箱email被注册过,就不往下处理并返回错误信息给用户.如果是新用户注册的话,就向数据库新增一条状态为不可用的用户记录,随后向用户邮箱发送一个校验的地址邮件.
- userIsExist是持久层一个函数.通过给它传入一个user_name字段它会生成sql语句去数据库中查询,判断数据库中有没有人已经注册过了这个用户名.持久层的函数实现的功能都是直接操作数据库,具体如何编写查看项目源码.
- 如果给userIsExist传入email字段,那么它就会拿着这个email去数据库中查询看有没有人注册过这个邮箱
- 在命令行工具输入npm i uuid --save,通过安装uuidv4这个包可以生成随机字符串,我们把它作为邮箱验证的校验码
- addUser是持久层新增用户的一个函数,它会在用户表中新加一条用户记录
- sendRegisterEmail函数负责给用户发送邮件.
/**
* 注册
*/
exports.register = async ({ user_name, password, email }) => {
//判断用户是否存在
const userInfo = await userIsExist({ user_name });
if (userInfo !== null && userInfo.status !== 1) {//userInfo.status决定该用户状态不可用
//用户已存在
return userExist();//自定义错误函数,返回值形似{"error_no": "100","message":"用户已存在"}
}
//判断邮箱是否存在
const emailInfo = await userIsExist({ email });
if (emailInfo !== null && emailInfo.status !== 1) {
//邮箱已存在
return emailExist();
}
//生成邮箱校验码
const verify_key = uuidv4();
const data = userInfo || emailInfo;
let result = null;
if (data) {
//已经存在用户数据了,但是该用户没有验证,所以重新发送一封邮件让用户验证
await updateUserInfo({
user_id: data.user_id,
verify_key,
});
sendRegisterEmail({ user_id: data.user_id, email, verify_key }); //发送校验邮箱
} else {
//新增用户
result = await addUser({
user_name,
password: md5(password),
email,
verify_key,//随机生成字符串
});
const { user_id, email, verify_key } = result;
sendRegisterEmail({ user_id, email, verify_key }); //发送校验邮箱
}
return new Success(result);//新增成功
};
邮箱校验
在上述用户注册结尾处,调用sendRegisterEmail({ user_id: data.user_id, email, verify_key })给用户邮箱发送邮件.其中verify_key是随机生成的校验码,它已经存到了该新增用户的数据库中.
接下来我们要构造一个链接如下,通过邮件的方式发送给用户让其点击完后最后的注册流程.
http://localhost:8081/regiter_success?id=5&verify_key=e408c17f-ec26-4847-a633-c39cd6621f49
通过观察这个链接,可以看出/register_success是需要单独开发的接口.id对应着user_id,verify_key是随机生成的字符串.如果用户一旦点击该链接,我们在此接口中就可以获取user_id和verify_key.通过user_id去数据库中查询用户记录,如果发现数据库中该用户记录存储的verify_key和接口中获取的verify_key是相等的话,就可以将该条记录的状态置为可用了.至此整个注册校验的流程才算落幕.
接下来深入研究sendRegisterEmail的函数该如何编写.如果想让nodejs服务器发送邮件,可以通过第三方的包nodemailer来帮我们轻易实现.运行npm i nodemailer --save安装依赖.随后创建一个新文件将nodemailer相关的API封装成工具方法sendRegisterEmail给外部调用.
const nodemailer = require('nodemailer');
const { service_ip } = require('../config');
const transporter = nodemailer.createTransport({
host: 'smtp.qq.com', //QQ邮箱的服务器
port: 587, //端口号
secure: false, //465为true,其他为false
auth: {
user: 'aaa@qq.com', // 自己的邮箱
pass: '-------', // 授权码,邮箱的授权码
},
});
/**
* 注册用户时发送邮箱
*/
exports.sendRegisterEmail = ({ user_id, email, verify_key }) => {
const url = `${service_ip}/regiter_success?id=${user_id}&verify_key=${verify_key}`;
const params = {
from: '奥巴马<aaa@qq.com>', // 收件人显示的发件人信息,xxxxxxx换成自己的qq
to: email, // 目标邮箱号
subject: '注册新用户',
html: `点击链接即可注册完毕:<a style="color:red" href="${url}">${url}</a>`,
};
return sendMsg(params);
};
/**
* 找回密码时发送校验码
* @param {*} params
*/
exports.sendCode = ({ email, verify_key }) => {
const params = {
from: '奥巴马<aaa@qq.com>', // 收件人显示的发件人信息,xxxxxxx换成自己的qq
to: email, // 目标邮箱号
subject: '找回密码',
html: `邮箱验证码:${verify_key}`,
};
return sendMsg(params);
};
/**
* 发送消息
*/
const sendMsg = (params) => {
return new Promise((resolve) => {
transporter.sendMail(params, (err, data) => {
resolve(null);
transporter.close(); //发送完毕后关闭
});
});
};
上面的工具方法里面有两个地方要更改,第一个aaa@qq.com要改成自己的qq邮箱地址,nodejs使用此邮箱给用户发邮件.第二个地方-------要填写改邮箱分发的授权码.
那如何获取授权码呢?以qq邮箱为例.点击邮箱的设置--账户--POP3/SMTP服务开启就能得到邮箱授权码了.
用户现在收到了邮件并且点击了邮箱的链接地址,这个地址接口/regiter_success需要我们继续开发.它里面所做的事情无非就是验证随机字符串的正确性以及将用户状态置为可用.
路由层:
/**
* 注册成功
*/
router.get('/regiter_success', async (ctx) => {
const { id: user_id, verify_key } = ctx.query;
ctx.body = await verifyKey({ user_id, verify_key });
});
业务层:
/**
* 注册时验证key值对不对
* @param {*} param0
*/
exports.verifyKey = async ({ user_id, verify_key }) => {
const userInfo = await userIsExist({ user_id, verify_key });
if (userInfo === null) {
return keyUnValid();//无效的校验码
}
await updateUserInfo({ //认证成功将verify_key置为"",并是用户状态status置为可用
verify_key: '',
user_id,
status: 0,
});
return new Success();
};
密码找回
密码找回使用的知识点和注册类似.用户想找回密码需要向后端发送两个字段,分别是邮箱email和新密码password,通过邮箱查到该用户的数据库记录重置密码即可.但是为了确保想修改密码的人是该邮箱的真实拥有者,所以得先给邮箱发送一个检验码code.用户除了要向后端发送上述两个字段外还需发送自己邮箱收到的校验码,后端收到此三个字段便可以校验用户的真实性并重置密码.