From a96ea0622f2a7025b6d9e86583af8764b3b10099 Mon Sep 17 00:00:00 2001 From: dead-horse Date: Tue, 13 Mar 2012 17:39:04 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=89=BE=E5=9B=9E?= =?UTF-8?q?=E5=AF=86=E7=A0=81=E9=80=BB=E8=BE=91=EF=BC=8C=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E8=B7=B3=E8=BD=AC=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controllers/sign.js | 127 +++++++++++++++++++++++++++++++----------- models/user.js | 5 +- routes.js | 1 + views/sign/reset.html | 42 ++++++++++++++ 4 files changed, 141 insertions(+), 34 deletions(-) create mode 100644 views/sign/reset.html diff --git a/controllers/sign.js b/controllers/sign.js index 24ba7fb..f9901e0 100644 --- a/controllers/sign.js +++ b/controllers/sign.js @@ -100,7 +100,14 @@ exports.showLogin = function(req, res) { req.session._loginReferer = req.headers.referer; res.render('sign/signin'); }; - +/** + * define some page when login just jump to the home page + * @type {Array} + */ +var notJump = [ + '/active_account', //active page + '/reset_pass' //reset password page, avoid to reset twice +]; /** * Handle user login. * @@ -131,7 +138,15 @@ exports.login = function(req, res, next) { } // store session cookie gen_session(user, res); - res.redirect(req.session._loginReferer || 'home'); + //check at some page just jump to home page + var refer = req.session._loginReferer || 'home'; + for (var i=0, len=notJump.length; i!=len; ++i) { + if (refer.indexOf(notJump[i]) >= 0) { + refer = 'home'; + break; + } + } + res.redirect(refer); }); }; @@ -179,35 +194,81 @@ exports.search_pass = function(req,res,next){ return; } - User.findOne({email:email},function(err,user){ - if(!user){ - res.render('sign/search_pass', {error:'没有这个电子邮箱。',email:email}); - return; - } - mail_ctrl.send_reset_pass_mail(email,md5(email+config.session_secret),user.name,function(err,success){ - res.render('notify/notify',{success: '我们已给您填写的电子邮箱发送了一封邮件,请点击里面的链接来重置密码。'}); + // User.findOne({email:email},function(err,user){ + //动态生成retrive_key和timestamp到users collection,之后重置密码进行验证 + var retrieveKey = randomString(15); + var retrieveTime = new Date().getTime(); + User.findOne({email : email}, function(err, user) { + if(!user) { + res.render('sign/search_pass', {error:'没有这个电子邮箱。',email:email}); + return; + } + user.retrieve_key = retrieveKey; + user.retrieve_time = retrieveTime; + user.save(function(err) { + if(err) { + return next(err); + } + mail_ctrl.send_reset_pass_mail(email, retrieveKey, user.name, function(err,success) { + res.render('notify/notify',{success: '我们已给您填写的电子邮箱发送了一封邮件,请点击里面的链接来重置密码。'}); + }); }); }); } } - -exports.reset_pass = function(req,res,next){ - var key = req.query.key; - var name = req.query.name; - var new_pass = ''; - - User.findOne({name:name},function(err,user){ - if(!user || md5(user.email+config.session_secret) != key){ - res.render('notify/notify',{error: '信息有误,密码无法重置。'}); - return; - } - new_pass = random_password(); - user.pass = md5(new_pass); - user.save(function(err){ - res.render('notify/notify',{success: '你的密码已被重置为:' + new_pass + ',请立即用此密码登录后在设置页面更改密码。'}); - }); - }); - +/** + * reset password + * 'get' to show the page, 'post' to reset password + * after reset password, retrieve_key&time will be destroy + * @param {http.req} req + * @param {http.res} res + * @param {Function} next + */ +exports.reset_pass = function(req,res,next) { + var method = req.method.toLowerCase(); + if(method === 'get') { + var key = req.query.key; + var name = req.query.name; + User.findOne({name:name, retrieve_key:key},function(err,user) { + if(!user) { + return res.render('notify/notify',{error: '信息有误,密码无法重置。'}); + } + var now = new Date().getTime(); + var oneDay = 1000 * 60 * 60 * 24; + console.log(user); + console.log(user.retrieve_time, now); + if(!user.retrieve_time || now - user.retrieve_time > oneDay) { + return res.render('notify/notify', {error : '该链接已过期,请重新申请。'}); + } + return res.render('sign/reset', {name : name, key : key}); + user.save(function(err) { + res.render('notify/notify',{success: '你的密码已被重置为:' + new_pass + ',请立即用此密码登录后在设置页面更改密码。'}); + }); + }); + } else { + var psw = req.body.psw || ''; + var repsw = req.body.repsw || ''; + var key = req.body.key || ''; + var name = req.body.name || ''; + if(psw !== repsw) { + return res.render('sign/reset', {name : name, key : key, error : '两次密码输入不一致。'}); + } + User.findOne({name:name, retrieve_key: key}, function(err, user) { + if(!user) { + return res.render('notify/notify', {error : '错误的激活链接'}); + } + user.pass = md5(psw); + user.retrieve_key = null; + user.retrieve_time = null; + user.save(function(err) { + if(err) { + return next(err); + } + console.log(user); + return res.render('notify/notify', {success: '你的密码已重置。'}); + }) + }) + } } // auth_user middleware @@ -250,30 +311,30 @@ exports.auth_user = function(req,res,next){ }; // private -function gen_session(user,res){ +function gen_session(user,res) { var auth_token = encrypt(user._id + '\t'+user.name + '\t' + user.pass +'\t' + user.email, config.session_secret); res.cookie(config.auth_cookie_name, auth_token, {path: '/',maxAge: 1000*60*60*24*7}); //cookie 有效期1周 } -function encrypt(str,secret){ +function encrypt(str,secret) { var cipher = crypto.createCipher('aes192', secret); var enc = cipher.update(str,'utf8','hex'); enc += cipher.final('hex'); return enc; } -function decrypt(str,secret){ +function decrypt(str,secret) { var decipher = crypto.createDecipher('aes192', secret); var dec = decipher.update(str,'hex','utf8'); dec += decipher.final('utf8'); return dec; } -function md5(str){ +function md5(str) { var md5sum = crypto.createHash('md5'); md5sum.update(str); str = md5sum.digest('hex'); return str; } -function random_password(passwd_size){ - var size = passwd_size || 6; +function randomString(size) { + size = size || 6; var code_string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; var max_num = code_string.length + 1; var new_pass = ''; diff --git a/models/user.js b/models/user.js index 91dcba5..82647b0 100644 --- a/models/user.js +++ b/models/user.js @@ -28,7 +28,10 @@ var UserSchema = new Schema({ receive_reply_mail: {type: Boolean, default: false }, receive_at_mail: { type: Boolean, default: false }, - from_wp: { type: Boolean } + from_wp: { type: Boolean }, + + retrieve_time : {type: Number}, + retrieve_key : {type: String} }); mongoose.model('User', UserSchema); diff --git a/routes.js b/routes.js index e79d27c..7a9b475 100644 --- a/routes.js +++ b/routes.js @@ -35,6 +35,7 @@ exports = module.exports = function(app) { app.get('/search_pass', sign.search_pass); app.post('/search_pass', sign.search_pass); app.get('/reset_pass', sign.reset_pass); + app.post('/reset_pass', sign.reset_pass); // user app.get('/user/:name', user.index); diff --git a/views/sign/reset.html b/views/sign/reset.html new file mode 100644 index 0000000..42a8fbd --- /dev/null +++ b/views/sign/reset.html @@ -0,0 +1,42 @@ +<%- partial('sign/sidebar') %> + +
+
+
+ +
+
+
+
+ <% if(locals.error){ %> +
+ × + <%= error %> +
+ <% } %> +
+
+ +
+ +
+
+
+ +
+ +
+
+ + + +
+ +
+
+
+
+
From 611e73e97c35aad14dfc3ee573dc664dd0588a87 Mon Sep 17 00:00:00 2001 From: dead-horse Date: Tue, 13 Mar 2012 17:40:23 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E6=89=BE=E5=9B=9E=E6=96=87=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controllers/mail.js | 2 +- controllers/sign.js | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/controllers/mail.js b/controllers/mail.js index d540aae..f6b6ad6 100644 --- a/controllers/mail.js +++ b/controllers/mail.js @@ -41,7 +41,7 @@ function send_reset_pass_mail(who,token,name,cb){ var to = who; var subject = config.name + '社区密码重置'; var html = '

您好:

' + - '

我们收到您在' + config.name + '社区重置密码的请求,请单击下面的链接来重置密码:

' + + '

我们收到您在' + config.name + '社区重置密码的请求,请在24小时内单击下面的链接来重置密码:

' + '重置密码链接' + '

若您没有在' + config.name + '社区填写过注册信息,说明有人滥用了您的电子邮箱,请删除此邮件,我们对给您造成的打扰感到抱歉。

' + '

' + config.name +'社区 谨上。

' diff --git a/controllers/sign.js b/controllers/sign.js index f9901e0..c4b616e 100644 --- a/controllers/sign.js +++ b/controllers/sign.js @@ -210,7 +210,7 @@ exports.search_pass = function(req,res,next){ return next(err); } mail_ctrl.send_reset_pass_mail(email, retrieveKey, user.name, function(err,success) { - res.render('notify/notify',{success: '我们已给您填写的电子邮箱发送了一封邮件,请点击里面的链接来重置密码。'}); + res.render('notify/notify',{success: '我们已给您填写的电子邮箱发送了一封邮件,请在24小时内点击里面的链接来重置密码。'}); }); }); }); @@ -235,8 +235,6 @@ exports.reset_pass = function(req,res,next) { } var now = new Date().getTime(); var oneDay = 1000 * 60 * 60 * 24; - console.log(user); - console.log(user.retrieve_time, now); if(!user.retrieve_time || now - user.retrieve_time > oneDay) { return res.render('notify/notify', {error : '该链接已过期,请重新申请。'}); } @@ -264,7 +262,6 @@ exports.reset_pass = function(req,res,next) { if(err) { return next(err); } - console.log(user); return res.render('notify/notify', {success: '你的密码已重置。'}); }) }) From 0bb2ecfe05b05ffd872da89102f35cdc53eb80c8 Mon Sep 17 00:00:00 2001 From: dead-horse Date: Tue, 13 Mar 2012 17:41:17 +0800 Subject: [PATCH 3/4] fix --- controllers/sign.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/controllers/sign.js b/controllers/sign.js index c4b616e..c40d519 100644 --- a/controllers/sign.js +++ b/controllers/sign.js @@ -239,9 +239,6 @@ exports.reset_pass = function(req,res,next) { return res.render('notify/notify', {error : '该链接已过期,请重新申请。'}); } return res.render('sign/reset', {name : name, key : key}); - user.save(function(err) { - res.render('notify/notify',{success: '你的密码已被重置为:' + new_pass + ',请立即用此密码登录后在设置页面更改密码。'}); - }); }); } else { var psw = req.body.psw || ''; From da440451848a751ba95185814bf56f45e536461c Mon Sep 17 00:00:00 2001 From: dead-horse Date: Tue, 13 Mar 2012 19:11:37 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E5=8F=91=E9=80=81=E9=82=AE=E4=BB=B6?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E4=B8=8D=E7=AD=89=E5=BE=85=E9=82=AE=E4=BB=B6?= =?UTF-8?q?=E5=8F=91=E9=80=81=E6=88=90=E5=8A=9F=E5=85=88=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E7=94=A8=E6=88=B7=EF=BC=8C=E9=81=BF=E5=85=8D=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=AD=89=E5=BE=85=E6=97=B6=E9=97=B4=E8=BF=87=E9=95=BF=E3=80=82?= =?UTF-8?q?=E9=82=AE=E4=BB=B6=E5=8E=8B=E5=85=A5=E9=98=9F=E5=88=97=EF=BC=8C?= =?UTF-8?q?=E5=A6=82=E6=9E=9C=E5=A4=B1=E8=B4=A51=E5=88=86=E9=92=9F?= =?UTF-8?q?=E5=90=8E=E5=86=8D=E6=AC=A1=E5=8F=91=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controllers/mail.js | 98 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 80 insertions(+), 18 deletions(-) diff --git a/controllers/mail.js b/controllers/mail.js index f6b6ad6..ea970c4 100644 --- a/controllers/mail.js +++ b/controllers/mail.js @@ -1,6 +1,7 @@ var mailer = require('nodemailer'); var config = require('../config').config; - +var EventProxy = require('eventproxy').EventProxy; +var util = require('util'); mailer.SMTP = { host: config.mail_host, port: config.mail_port, @@ -9,13 +10,75 @@ mailer.SMTP = { pass: config.mail_pass }; -function send_mail(data,cb){ - mailer.send_mail(data,function(err,success){ - return cb(err,success); - }); +/** + * keep all the mails to send + * @type {Array} + */ +var mails = []; +var timer; +/** + * control mailer + * @type {EventProxy} + */ +var mailEvent = new EventProxy(); +/** + * when need to send an email, start to check the mails array and send all of emails. + */ +mailEvent.on("getMail", function() { + if(mails.length === 0) { + return; + } else { + //遍历邮件数组,发送每一封邮件,如果有发送失败的,就再压入数组,同时触发mailEvent事件 + var failed = false; + for(var i = 0, len = mails.length; i != len; ++i) { + var message = mails[i]; + mails.splice(i, 1); + i--; + len--; + var mail; + try { + message.debug = false; + mail = mailer.send_mail(message, function(error, success) { + if(error) { + mails.push(message); + failed = true; + } + }); + } catch(e) { + mails.push(message); + failed = true; + } + if(mail) { + var oldemit = mail.emit; + mail.emit = function() { + oldemit.apply(mail, arguments); + } + } + } + if(failed) { + clearTimeout(timer); + timer = setTimeout(trigger, 60000); + } + } +}); + +/** + * trigger email event + * @return {[type]} + */ +function trigger() { + mailEvent.trigger("getMail"); +} +/** + * send an email + * @param {mail} data [info of an email] + */ +function send_mail (data) { + mails.push(data); + trigger(); } -function send_active_mail(who,token,name,email,cb){ +function send_active_mail(who, token, name, email, cb) { var sender = config.mail_sender; var to = who; var subject = config.name + '社区帐号激活'; @@ -31,12 +94,10 @@ function send_active_mail(who,token,name,email,cb){ subject: subject, html: html } - - send_mail(data,function(err,success){ - return cb(err,success); - }); + cb (null, true); + send_mail(data); } -function send_reset_pass_mail(who,token,name,cb){ +function send_reset_pass_mail(who, token, name, cb) { var sender = config.mail_sender; var to = who; var subject = config.name + '社区密码重置'; @@ -53,11 +114,11 @@ function send_reset_pass_mail(who,token,name,cb){ html: html } - send_mail(data,function(err,success){ - return cb(err,success); - }); + cb (null, true); + send_mail(data); } -function send_reply_mail(who,msg){ + +function send_reply_mail(who, msg) { var sender = config.mail_sender; var to = who; var subject = config.name + ' 新消息'; @@ -76,10 +137,11 @@ function send_reply_mail(who,msg){ html: html } - send_mail(data,function(err,success){}); + send_mail(data); } -function send_at_mail(who,msg){ + +function send_at_mail(who, msg) { var sender = config.mail_sender; var to = who; var subject = config.name + ' 新消息'; @@ -98,7 +160,7 @@ function send_at_mail(who,msg){ html: html } - send_mail(data,function(err,success){}); + send_mail(data); } exports.send_active_mail = send_active_mail;