用邮箱激活账号,在注册的时候必须先让用户输入个人邮箱。先把准备工作做好。
在注册页面添加邮箱的输入
// ./src/views/signup.html
<h1>注册</h1>
<div class="form-group">
<input type="text" class="form-control" v-model="name" placeholder="账号">
</div>
<div class="form-group">
<input type="email" class="form-control" v-model="email" placeholder="邮箱">
</div>
...
相应的,在 Vue 的 data 以及 axios.post('/api/v1/signup')部分添加 email,以保证 email 能正常送到服务端。
// ./src/views/signup.html
var vm = new Vue({
el: '#app',
data: {
name: '',
email: '',
pass: '',
rePass: ''
},
methods: {
submit () {
axios.post('/api/v1/signup',
{
name: vm.name,
email: vm.email,
pass: vm.pass,
rePass: vm.rePass
})
.then(function(response) {
return response.data;
})
.then(function(data) {
alert(data.message);
})
.catch(function(err) {
alert(err.response.data.error);
})
}
}
});
</script>
在服务端处理注册逻辑时,也要增加对 email 新字段的处理,这一块可以参考一下代码。
./src/controllers/user.js
./src/models/user.js
当注册时,增加了 email 后,我们可以在 user 表中增加一个字段 active 来标识该邮箱是否被激活。
// ./src/models/user.js
import mongoose from 'mongoose';
const Schema = mongoose.Schema;
const UserSchema = new Schema({
name: String,
email: String,
pass: String,
active: Boolean
});
const UserModel = mongoose.model('User', UserSchema);
export default UserModel;
当用户的基本信息具有 active 属性后,我们可以用 active 来阻挡用户的一些操作。比如,如果用户登陆了不是激活状态,可以直接报错。
// ./src/middlewares/auth.js
export const userRequired = (req, res, next) => {
if (!req.user) {
let err = new Error('需要登录');
err.status = 403;
next(err);
return;
}
if (!req.user.active) {
let err = new Error('需要激活');
err.status = 403;
next(err);
return;
}
next();
};
export const adminRequired = (req, res, next) => {
if (!req.user) {
let err = new Error('需要登录');
err.status = 403;
next(err);
return;
}
if (!req.user.active) {
let err = new Error('需要激活');
err.status = 403;
next(err);
return;
}
if (!req.user.isAdmin) {
let err = new Error('需要管理员权限');
err.status = 403;
next(err);
return;
}
next();
};
激活的处理原理并不是很难。
- 在注册时,我们可以把 email 和 pass 拼接在一起后生成一个 md5 字符串,这个 md5 字符串可以称为激活用的 token。
- 用 key 拼一个 url。比如:localhost:3000/activeAccount?key=token&name=name
- 给用户发一封邮件,邮件内容是包含
<a href="localhost:3000/activeAccount?key=token&name=name">激活</a>
。
如果用户在注册的时候填错了邮箱,这个激活的 url 也就发给了别人。所以,在注册时要提示用户填写自己的邮箱。
完成以上需要安装3个第三方库。
$ npm install --save nodemailer
$ npm install --save nodemailer-smtp-transport
$ npm install --save utility // md5 库
在注册成功后,主动发送激活邮件。
// ./src/controllers/user.js
import { sendActiveMail } from '../common/mail';
import utility from 'utility';
export const signup = function (req, res, next) {
const { name, email, pass, rePass } = req.body;
if (pass !== rePass) {
return next(new Error('两次密码不对'));
}
const user = new UserModel();
user.name = name;
user.email = email;
user.pass = bcrypt.hashSync(pass, 10);
user.save(function (err) {
if (err) {
next(err);
} else {
sendActiveMail(
email,
utility.md5(user.email + user.pass),
name
);
res.json({
message: `欢迎加入${
config.name
}!我们已给您的注册邮箱发送了一封邮件, 请点击里面的链接来激活您的帐号。`
});
}
});
};
设计一个简单的发送邮件函数。如果你对发送邮件的处理不是很明白,可以先单独试验一下 例子
// ./src/common/mail.js
import mailer from 'nodemailer';
import smtpTransport from 'nodemailer-smtp-transport';
import config from '../config';
const transporter = mailer.createTransport(smtpTransport(config.email));
const SITE_ROOT_URL = `${config.url}`;
function sendMail(data) {
const from = '<' + config.email.auth.user + '>';
data.from = from;
transporter.sendMail(data, err => {
if (err) {
console.log(err);
}
});
}
export function sendActiveMail(who, token, name) {
const to = who;
const subject = `${config.name}社区帐号激活`;
const html =
`<p>您好:${name}</p>` +
`<p>我们收到您在${config.name}社区的注册信息,请点击下面的链接来激活帐户:</p>` +
`<a href = "${SITE_ROOT_URL}/api/v1/activeAccount?key=${token}&name=${name}">激活链接</a>` +
`<p>若您没有在${config.name}社区填写过注册信息,说明有人滥用了您的电子邮箱,请删除此邮件,我们对给您造成的打扰感到抱歉。</p>` +
`<p>${config.name} 谨上。</p>`;
sendMail({
to,
subject,
html
});
}
在发送邮件时会用到发送邮箱以及一些邮箱配置信息,还有一些为了丰富激活邮件的配置信息,这些信息都放在 config.js 中。
./src/config.js
export default {
cookieName: 'your_cookie_name',
jwtSecret: 'your_jwt_secret',
mongodbUrl: 'mongodb://localhost:27017/firstapp',
admin: 'admin',
sessionSecret: 'your_session_secret',
email: {
host: 'smtp.qq.com',
port: 465,
auth: {
user: '[email protected]',
pass: 'your pass',
}
},
name: '我的社区',
url: 'localhost:3000'
}
用户会触发了邮件中的 激活,所以,服务器端要对路由 /activeAccount 增加激活处理。
激活处理的主要逻辑是将对用用户在数据库中的 email和密码再次 md5一下,和路由发过来的 token 进行比较,如果一致,说明是本人触发的激活。
最后只要将 active 更新为 true 即可。
添加路由
// ./src/route.api.js
/* GET active account */
router.get('/activeAccount', user.activeAccount);
激活处理
export const activeAccount = function (req, res, next) {
const { key, name } = req.query;
UserModel.findOne({ name }, function (err, user) {
if (err || !user) {
return next(new Error('找不到用户'));
} else {
const key2 = utility.md5(user.email + user.pass);
if (key !== key2) {
return next(new Error('激活失败'));
}
user.active = true;
user.save();
const token = jwt.encode(
{
_id: user._id,
name: user.name,
isAdmin: user.name === config.admin,
active: user.active,
exp: moment()
.add('days', 30)
.valueOf()
},
config.jwtSecret
);
const opts = {
path: '/',
maxAge: moment()
.add('days', 30)
.valueOf(),
signed: true,
httpOnly: true
};
res.cookie(config.cookieName, token, opts);
res.send('active successed!');
}
});
};
在更新完 active = true 后,再一次更新了 jwt 信息,目的是为了客户端能拿到最新的用户信息。