我在nodejs中使用passportjs来创建登录系统.一切都很好,但我不知道如何在忘记密码或想要更改密码时重置用户密码.
MongoDB中的用户模型
var UserSchema = new Schema({ email: String, username: String, provider: String, hashed_password: String, salt: String, });
提前致谢!
我真的不喜欢命中我的数据库来存储令牌,特别是当你想要为许多动作创建和验证令牌时.
相反,我决定复制Django如何做到这一点:
将timestamp_today转换为base36 as today
将user.id转换为base36 as ident
创建hash
包含:
timestamp_today
用户身份
user.last_login
用户密码
user.email
用隐藏的秘密来填充哈希
创建一个路由,如:/ change-password/:ident
/ :today
-:hash
我们测试req.params.timestamp,以便测试它是否适用于今天,最便宜的测试.先失败了.
然后我们找到用户,如果它不存在则失败.
然后我们从上面再次生成哈希,但是来自req.params的时间戳
如果出现以下情况,重置链
他们记得他们的密码和登录(last_login更改)
他们实际上仍然登录并且:
只需更改密码(密码更改)
只需更改电子邮件(电子邮件更改)
明天到了(时间戳变化太大)
这条路:
你没有在数据库中存储这些短暂的东西
当令牌的目的是改变事物的状态,并且事物状态改变时,令牌的目的不再是安全相关的.
我尝试使用node-password-reset作为Matt617建议,但并不真正关心它.这是目前搜索中唯一出现的问题.
所以一些时间挖掘,我发现自己更容易实现这一点.最后,我花了大约一天的时间来获取所有路线,用户界面,电子邮件和所有工作.我仍然需要增强安全性(重置计数器以防止滥用等),但是基本工作正常:
创建了两个新路由,/ forgot和/ reset,这些路由不需要用户登录即可访问.
GET on/forgot显示一个带有一个电子邮件输入的UI.
POST /忘记检查是否有用户具有该地址并生成随机令牌.
使用令牌和到期日更新用户的记录
发送电子邮件至/ reset/{token}的链接
GET on/reset/{token}检查是否存在具有该令牌但尚未过期的用户,然后显示具有新密码条目的UI.
POST/reset(发送新的pwd和令牌)检查是否存在具有该令牌但尚未过期的用户.
更新用户密码.
将用户的令牌和到期日期设置为null
这是我生成令牌的代码(取自node-password-reset):
function generateToken() { var buf = new Buffer(16); for (var i = 0; i < buf.length; i++) { buf[i] = Math.floor(Math.random() * 256); } var id = buf.toString('base64'); return id; }
希望这可以帮助.
编辑:这是app.js. 注意我将整个用户对象保留在会话中.我计划将来搬到沙发基地或类似的地方.
var express = require('express'); var path = require('path'); var favicon = require('static-favicon'); var flash = require('connect-flash'); var morgan = require('morgan'); var cookieParser = require('cookie-parser'); var cookieSession = require('cookie-session'); var bodyParser = require('body-parser'); var http = require('http'); var https = require('https'); var fs = require('fs'); var path = require('path'); var passport = require('passport'); var LocalStrategy = require('passport-local').Strategy; var app = express(); app.set('port', 3000); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'jade'); var cookies = cookieSession({ name: 'abc123', secret: 'mysecret', maxage: 10 * 60 * 1000 }); app.use(cookies); app.use(favicon()); app.use(flash()); app.use(morgan()); app.use(bodyParser.json()); app.use(bodyParser.urlencoded()); app.use(cookieParser()); app.use(passport.initialize()); app.use(passport.session()); app.use(express.static(path.join(__dirname, 'public'))); module.exports = app; passport.use(new LocalStrategy(function (username, password, done) { return users.validateUser(username, password, done); })); //KEEP ENTIRE USER OBJECT IN THE SESSION passport.serializeUser(function (user, done) { done(null, user); }); passport.deserializeUser(function (user, done) { done(null, user); }); //Error handling after everything else app.use(logErrors); //log all errors app.use(clientErrorHandler); //special handler for xhr app.use(errorHandler); //basic handler http.createServer(app).listen(app.get('port'), function () { console.log('Express server listening on HTTP port ' + app.get('port')); });
编辑:这是路线.
app.get('/forgot', function (req, res) { if (req.isAuthenticated()) { //user is alreay logged in return res.redirect('/'); } //UI with one input for email res.render('forgot'); }); app.post('/forgot', function (req, res) { if (req.isAuthenticated()) { //user is alreay logged in return res.redirect('/'); } users.forgot(req, res, function (err) { if (err) { req.flash('error', err); } else { req.flash('success', 'Please check your email for further instructions.'); } res.redirect('/'); }); }); app.get('/reset/:token', function (req, res) { if (req.isAuthenticated()) { //user is alreay logged in return res.redirect('/'); } var token = req.params.token; users.checkReset(token, req, res, function (err, data) { if (err) req.flash('error', err); //show the UI with new password entry res.render('reset'); }); }); app.post('/reset', function (req, res) { if (req.isAuthenticated()) { //user is alreay logged in return res.redirect('/'); } users.reset(req, res, function (err) { if (err) { req.flash('error', err); return res.redirect('/reset'); } else { req.flash('success', 'Password successfully reset. Please login using new password.'); return res.redirect('/login'); } }); });