我用护照创建了一个简单的身份验证应用程序(请参阅下面的代码)。通过会话中间件快速,在请求客户端尚未拥有会话的每个请求上创建会话。我想在登录后分配会话,或者在登录后创建新会话。
这是因为我最终将通过HTTPS进行登录,并希望防止黑客劫持已经过身份验证的用户的会话。
这是我的服务器代码:
// Server.js configures the application and sets up the webserver
//importing our modules
var express = require('express');
var app = express();
var port = process.env.PORT || 8080;
var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');
var MongoStore = require('connect-mongo')(express);
var configDB = require('./config/database.js');
//Configuration of Databse and App
mongoose.connect(configDB.url); //connect to our database
require('./config/passport')(passport); //pass passport for configuration
app.configure(function() {
//set up our express application
app.use(express.logger('dev')); //log every request to the console
app.use(express.cookieParser()); //read cookies (needed for auth)
app.use(express.bodyParser()); //get info from html forms
app.set('view engine', 'ejs'); //set up ejs for templating
//configuration for passport
app.use(express.session({ secret: 'olhosvermdfgytuelhoseasenhaclassica',
cookie: {
maxAge: 120000 },
store:
new MongoStore({
db: 'xYrotr4h',
host: 'novus.modulusmongo.net',
port: 27017,
username: 'gdog',
password: 'fakepassowrd123'
})
})); //session secret + expiration + store
app.use(passport.initialize());
app.use(passport.session()); //persistent login session
app.use(flash()); //use connect-flash for flash messages stored in session
});
//Set up routes
require('./app/routes.js')(app, passport);
//launch
app.listen(port);
console.log("Server listening on port" + port);
在我的新Passport本地策略中,当用户成功验证数据库但导致服务器崩溃时,我尝试使用req.session.regenerate()或req.session.reload()。
以下是我定义策略的方法:
//Passport.js sets up our local strategies
//imports
var LocalStrategy = require('passport-local').Strategy;
var User = require('../app/models/user');
//export this as a module since we give it to passport
module.exports = function(passport) {
//Set up the session for persistent login
passport.serializeUser(function(user, done) {
done(null, user.id);
});
//used to serialize the user
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
//setting up local sign up
passport.use('local-signup', new LocalStrategy({
//by default, the local strategy uses usernames and password, we will override with email
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
function(req, email, password, done) {
console.log("Callback ran!");
//asynchronous
//User.findOne wont fire unless data is sent back
process.nextTick(function() {
console.log("I did run!");
//find user whose email is the same as form email
// we are checking to see if the user trying to sign up already exists
User.findOne({ 'local.email': email }, function(err, user) {
//if there any errors, return the errors
if (err) {
return done(err);
}
//check to see if there any users already with that email
if (user) {
return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
} else {
console.log('New user will be added to the DB!');
//if there is no user with that e-mail, create the user
var newUser = new User();
//we set the user's local credentials
newUser.local.email = email;
newUser.local.password = newUser.generateHash(password);
//save the user in the store
newUser.save(function(err) {
if (err) {
throw err;
}
return done(null, newUser);
});
}
});
});
}));
// =========================================================================
// LOCAL LOGIN =============================================================
// =========================================================================
// we are using named strategies since we have one for login and one for signup
// by default, if there was no name, it would just be called 'local'
passport.use('local-login', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) { // callback with email and password from our form
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'local.email' : email }, function(err, user) {
// if there are any errors, return the error before anything else
if (err)
return done(err);
// if no user is found, return the message
if (!user)
return done(null, false, req.flash('loginMessage', 'No user found.')); // req.flash is the way to set flashdata using connect-flash
// if the user is found but the password is wrong
if (!user.validPassword(password))
return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.')); // create the loginMessage and save it to session as flashdata
// all is well, return successful user
// removing the req.session.regenerate fixes any crashing
req.session.regenerate(function(err, done, user){
return done(null, user);
});
});
}));
};
答案 0 :(得分:16)
在深入了解护照和快递会图书馆后,我发现了它!
var session = function (req, res) {
var temp = req.session.passport; // {user: 1}
req.session.regenerate(function(err){
//req.session.passport is now undefined
req.session.passport = temp;
req.session.save(function(err){
res.send(200);
});
});
};
app.post('/login', passport.authenticate('local'), session);
基本上我让护照先进行身份验证,然后将对象附加到req.session.passport。 Passport使用此对象查找会话映射 - > userId进一步请求。重新生成会话时,req.session.passport对象将丢失。因此,您必须确保将其转移到新生成的会话,并保存它。
答案 1 :(得分:0)
看起来Jared并不想直接根据issue #194来支持这一点,我不确定我是否同意 - 至少,护照应该暴露自己的会话重新生成功能。无论如何,你可以通过替换:
来解决这个问题req.session.regenerate(function(err, done, user){
return done(null, user);
});
有这样的事情:
var passport = req._passport.instance;
req.session.regenerate(function(err, done, user) {
req.session[passport._key] = {};
req._passport.instance = passport;
req._passport.session = req.session[passport._key];
return done(null, user);
});
答案 2 :(得分:0)
Gitter.im开源项目中的真实示例:https://gitlab.com/gitlab-org/gitter/webapp/commit/44bb6d8934bce37b86d4ee3fcdba759967a5e5c1
扩展Steven Yang's答案:
在使用custom callbacks的情况下,您有多种策略,您想创建一个单独的方法(例如passportLogin
),该方法最终将调用req.login
:
//passportLogin.js
async function regeneratePassportSession(req) {
const passportSession = req.session.passport;
return new Promise((resolve, reject) =>
req.session.regenerate(function(err) {
if (err) reject(err);
assert(!req.session.passport);
req.session.passport = passportSession;
req.session.save(function(err) {
if (err) reject(err);
resolve();
});
})
);
}
/**
* Adds user to passport, if this is the
* first time (user just logged in) we generate a new session
* and returns a user with identity object
*/
module.exports = async function passportLogin(req, user) {
// if user just logged in (session hasn't been authenticated before)
if (!req.user) await regeneratePassportSession(req);
await new Promise((resolve, reject) => {
req.login(user, err => {
if (err) reject(err);
resolve();
});
});
return user;
在每一个策略中都称呼它。
答案 3 :(得分:-1)
我认为你需要替换:
req.session.regenerate(function(err, done, user){
return done(null, user);
});
使用:
req.login(user, function(err) {
if (err) return res.status(500).send('error');
return done(null,user);
});
req.login
在内部调用您的passport.serializeUser()
功能。