我尝试按照 Matt617 的建议使用 node-password-reset,但并不真正关心它。这是目前搜索中唯一出现的内容。
经过几个小时的研究,我发现自己实现这一点更容易。最后,我花了大约一天的时间让所有的路线、用户界面、电子邮件和一切正常工作。我仍然需要稍微增强安全性(重置计数器以防止滥用等),但基础工作正常:
- 创建了两个新路由,/forgot 和 /reset,不需要用户登录即可访问。
- /forgot 上的 GET 会显示带有一个电子邮件输入的 UI。
- /forgot 上的 POST 检查是否存在具有该地址的用户并生成随机令牌。
- 使用令牌和到期日期更新用户记录
- 发送一封电子邮件,其中包含指向 /reset/{token} 的链接
- /reset/{token} 上的 GET 检查是否有用户使用该令牌未过期,然后显示带有新密码条目的 UI。
- /reset 上的 POST(发送新密码和令牌)检查是否有用户使用该令牌尚未过期。
这是我生成令牌的代码(取自 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');
}
});
});