【问题标题】:Creating a new session after authentication with Passport使用 Passport 进行身份验证后创建新会话
【发布时间】:2019-09-05 19:28:19
【问题描述】:

我创建了一个带有护照的简单身份验证应用程序(请参见下面的代码)。通过会话中间件表达,在请求客户端还没有会话的每个请求上创建一个会话。我只想在登录后分配会话或在登录后创建新会话。

这是因为我最终将通过 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);
                 });

        });

    }));

};

【问题讨论】:

  • 如果黑客可以劫持用户的会话,他们就不能劫持新生成的会话吗?
  • 我相信他们至少会很难区分经过身份验证的会话和未经身份验证的会话。并且经过身份验证的会话将通过 HTTPS。

标签: node.js session authentication passport.js


【解决方案1】:

在研究了护照和快速会话库之后,我想通了!

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);

基本上我让passport首先进行身份验证,它将一个对象附加到req.session.passport上。 Passport 使用这个对象在进一步的请求中找出从 session -> userId 的映射。当您重新生成会话时, req.session.passport 对象会丢失。因此,您必须确保将其转移到新生成的会话中并保存。

【讨论】:

  • 太棒了,谢谢!这应该是答案......我在这个问题上挣扎了很多,我想写我自己的passport.js :-)
  • 这确实应该是公认的答案;这是确切的潜在需求。
【解决方案2】:

看起来 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);
});

【讨论】:

    【解决方案3】:

    来自 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;
    
    

    并在每个策略中调用它。

    【讨论】:

      【解决方案4】:

      我认为你需要更换:

      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() 函数。

      【讨论】:

        猜你喜欢
        • 2015-05-18
        • 2021-10-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-01-31
        • 1970-01-01
        • 2014-10-30
        • 2014-10-20
        相关资源
        最近更新 更多