护照 + NodeJs + Express 中 "req.user" 未定义

6

我遇到了一个会话问题,在成功使用passport.authenticate方法后,获取不到req.user

基本上,在注册或登录后,当我重定向时,无法在请求变量中找到"user"。

我使用了一个黑客马拉松的入门者,它使用了Mongo,我试图改变一些东西以使用Postgres。

编辑--根据评论的建议

现在注册流程正常工作,但是登录流程存在一些问题。有些奇怪的事情发生了,当我添加了很多断点时,似乎会间歇性地进行登录。按照评论的建议更新代码

app.js

const express = require('express');
const compression = require('compression');
const session = require('express-session');
const bodyParser = require('body-parser');
const logger = require('morgan');
const chalk = require('chalk');
const errorHandler = require('errorhandler');
const lusca = require('lusca');
const dotenv = require('dotenv');
const flash = require('express-flash');
const path = require('path');
const passport = require('passport');
const expressValidator = require('express-validator');
const expressStatusMonitor = require('express-status-monitor');
const sass = require('node-sass-middleware');
const multer = require('multer');

dotenv.load({ path: '.env.example' });

const SequelizeStore = require('connect-session-sequelize')(session.Store);
const models = require('./models');

const upload = multer({ dest: path.join(__dirname, 'uploads') });
/**
 * Load environment variables from .env file, where API keys and passwords are configured.
 */

/**
 * Controllers (route handlers).
 */
const homeController = require('./controllers/home');
const userController = require('./controllers/user');
const contactController = require('./controllers/contact');
const dashController = require('./controllers/dash');
const currencyController = require('./controllers/currency');
const accountController = require('./controllers/account');
const testController = require('./controllers/test');
const txController = require('./controllers/transaction');

/**
 * API keys and Passport configuration.
 */
const passportConfig = require('./config/passport');

/**
 * Create Express server.
 */
const app = express();


/**
 * Express configuration.
 */
app.set('host', '127.0.0.1');
app.set('port', 8080);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(expressStatusMonitor());
app.use(compression());
app.use(sass({
  src: path.join(__dirname, 'public'),
  dest: path.join(__dirname, 'public')
}));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressValidator());
app.use(session({
  resave: true,
  saveUninitialized: true,
  secret: process.env.SESSION_SECRET,
  cookie: { maxAge: 1209600000 }, // two weeks in milliseconds
  store: new SequelizeStore({
    db: models.sequelize
  })
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
app.use((req, res, next) => {
  // if (req.path === '/api/upload') {
  //   next();
  // } else {
  //   lusca.csrf()(req, res, next);
  // }
  next();
});
app.use(lusca.xframe('SAMEORIGIN'));
app.use(lusca.xssProtection(true));
app.disable('x-powered-by');
app.use((req, res, next) => {
  res.locals.user = req.user;
  next();
});
app.use((req, res, next) => {
  // After successful login, redirect back to the intended page
  if (!req.user
    && req.path !== '/login'
    && req.path !== '/signup'
    && !req.path.match(/^\/auth/)
    && !req.path.match(/\./)) {
    req.session.returnTo = req.originalUrl;
  } else if (req.user
    && (req.path === '/account' || req.path.match(/^\/api/))) {
    req.session.returnTo = req.originalUrl;
  }
  next();
});
app.use('/', express.static(path.join(__dirname, 'public'), { maxAge: 31557600000 }));
app.use('/js/lib', express.static(path.join(__dirname, 'node_modules/chart.js/dist'), { maxAge: 31557600000 }));
app.use('/js/lib', express.static(path.join(__dirname, 'node_modules/popper.js/dist/umd'), { maxAge: 31557600000 }));
app.use('/js/lib', express.static(path.join(__dirname, 'node_modules/bootstrap/dist/js'), { maxAge: 31557600000 }));
app.use('/js/lib', express.static(path.join(__dirname, 'node_modules/jquery/dist'), { maxAge: 31557600000 }));
app.use('/webfonts', express.static(path.join(__dirname, 'node_modules/@fortawesome/fontawesome-free/webfonts'), { maxAge: 31557600000 }));

/**
 * Primary app routes.
 */
app.get('/', homeController.index);
app.get('/login', userController.getLogin);
app.post('/login', userController.postLogin);
app.get('/logout', userController.logout);
app.get('/forgot', userController.getForgot);
app.post('/forgot', userController.postForgot);
app.get('/reset/:token', userController.getReset);
app.post('/reset/:token', userController.postReset);
app.get('/signup', userController.getSignup);
app.post('/signup', userController.postSignup);
// app.get('/account', passportConfig.isAuthenticated, userController.getAccount);
// app.post('/account/profile', passportConfig.isAuthenticated, userController.postUpdateProfile);
app.post('/account/password', passportConfig.isAuthenticated, userController.postUpdatePassword);
// app.post('/account/delete', passportConfig.isAuthenticated, userController.postDeleteAccount);
// app.get('/account/unlink/:provider', passportConfig.isAuthenticated, userController.getOauthUnlink);
app.get('/dashboard', passportConfig.isAuthenticated, dashController.index);
app.get('/test', passportConfig.isAuthenticated, testController.test);
// app.get('/contact', contactController.contact);
// app.post('/addContact', contactController.addContact);
// app.post('/editContact', contactController.editContact);
// app.get('/listContacts', contactController.listContacts);
// app.get('/listCurrency', currencyController.listCurrency);
// app.post('/addAccount', accountController.addAccount);
// app.post('/editAccount', accountController.editAccount);
// app.get('/listAccounts', accountController.listAccounts);
// app.get('/getTransactions', txController.getTransactions);


/**
 * Error Handler.
 */
if (process.env.NODE_ENV === 'development') {
  // only use in development
  app.use(errorHandler());
} else {
  app.use((err, req, res, next) => {
    console.error(err);
    res.status(500).send('Server Error');
  });
}

/**
 * Start Express server.
 */

models.sequelize.sync({}).then(() => {
  app.listen(app.get('port'), () => {
    console.log('%s App is running at http://localhost:%d in %s mode', chalk.green('✓'), app.get('port'), app.get('env'));
    console.log('Press CTRL-C to stop\n');
  });
});

module.exports = app;

user.js

const { promisify } = require('util');
const crypto = require('crypto');
const nodemailer = require('nodemailer');
const passport = require('passport');
const models = require('../models');

const { User } = models;

const randomBytesAsync = promisify(crypto.randomBytes);

/**
 * GET /login
 * Login page.
 */
exports.getLogin = (req, res) => {
  if (req.user) {
    return res.redirect('/');
  }
  res.render('account/login', {
    title: 'Login'
  });
};

/**
 * POST /login
 * Sign in using email and password.
 */
exports.postLogin = (req, res, next) => {
  req.assert('email', 'Email is not valid').isEmail();
  req.assert('password', 'Password cannot be blank').notEmpty();
  req.sanitize('email').normalizeEmail({ gmail_remove_dots: false });

  const errors = req.validationErrors();

  if (errors) {
    req.flash('errors', errors);
    return res.redirect('/login');
  }

  passport.authenticate('local', (err, user, info) => {
    if (err) { return next(err); }
    if (!user) {
      req.flash('errors', info);
      return res.redirect('/login');
    }
    req.logIn(user, (err) => {
      if (err) { return next(err); }
res.locals.user = user; //Updated code after comment suggestions
      req.flash('success', { msg: 'Success! You are logged in.' });
      res.redirect('/dashboard');
    });
  })(req, res, next);
};

/**
 * GET /logout
 * Log out.
 */
exports.logout = (req, res) => {
  req.logout();
  req.session.destroy((err) => {
    if (err) console.log('Error : Failed to destroy the session during logout.', err);
    req.user = null;
    res.redirect('/');
  });
};

/**
 * GET /signup
 * Signup page.
 */
exports.getSignup = (req, res) => {
  if (req.user) {
    return res.redirect('/');
  }
  res.render('account/signup', {
    title: 'Create Account'
  });
};

/**
 * POST /signup
 * Create a new local account.
 */
exports.postSignup = (req, res, next) => {
  req.assert('email', 'Email is not valid').isEmail();
  req.assert('password', 'Password must be at least 4 characters long').len(4);
  req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password);
  req.sanitize('email').normalizeEmail({ gmail_remove_dots: false });

  const errors = req.validationErrors();

  if (errors) {
    req.flash('errors', errors);
    return res.redirect('/signup');
  }
  User.findAll({ limit: 1, where: { email: req.body.email }, plain: true })
    .then((existingUser) => {
      if (existingUser) {
        req.flash('errors', { msg: 'Account with that email address already exists.' });
        return res.redirect('/signup');
      }
      User.create({
        email: req.body.email,
        password: req.body.password
      }).then((user) => {
        req.logIn(user, (err) => {
          if (err) { return next(err); }
res.locals.user = user; //updated code after comment suggestions 
          return res.redirect('/');
        });
      }).catch(error => next(error));
    });
};

passport.js

const passport = require('passport');
const request = require('request');
const { Strategy: LocalStrategy } = require('passport-local');
const _ = require('lodash');

const models = require('../models');

passport.serializeUser((user, done) => {
  done(null, user.id);
});

passport.deserializeUser((id, done) => {
  models.User.findAll({ where: { id }, limit: 1, plain: true })
    .then(user => done(null, user))
    .catch(err => done(err));
});

/**
 * Sign in using Email and Password.
 */
passport.use(new LocalStrategy({ usernameField: 'email' }, (email, password, done) => {
  models.User.findAll({ where: { email }, limit: 1, plain: true }).then((user) => {
    if (!user) {
      return done(null, false, { msg: `Email ${email} not found.` });
    }
    user.comparePassword(password, (err, isMatch) => {
      if (err) { return done(err); }
      if (isMatch) {
        return done(null, user);
      }
      return done(null, false, { msg: 'Invalid email or password.' });
    });
  }).catch(error => done(error));
}));

/**
 * OAuth Strategy Overview
 *
 * - User is already logged in.
 *   - Check if there is an existing account with a provider id.
 *     - If there is, return an error message. (Account merging not supported)
 *     - Else link new OAuth account with currently logged-in user.
 * - User is not logged in.
 *   - Check if it's a returning user.
 *     - If returning user, sign in and we are done.
 *     - Else check if there is an existing account with user's email.
 *       - If there is, return an error message.
 *       - Else create a new account.
 */

/**
 * Login Required middleware.
 */
exports.isAuthenticated = (req, res, next) => {
  if (req.isAuthenticated()) {
    return next();
  }
  res.redirect('/login');
};


/**
 * Authorization Required middleware.
 */
exports.isAuthorized = (req, res, next) => {
  const provider = req.path.split('/').slice(-1)[0];
  const token = req.user.tokens.find(token => token.kind === provider);
  if (token) {
    next();
  } else {
    res.redirect(`/auth/${provider}`);
  }
};

1
可能是重复的问题:Node + Express + Passport: req.user Undefined - node_modules
太好了,它起作用了。谢谢你。另外,你能解释一下为什么需要这个吗?我在重定向之前添加了“res.locals.user = user;”,这样做的方式是正确的。 - Gaurav Agrawal
很高兴它对你有用。这就是我如何在请求中传递我的用户对象的方式。更多解释请参见:app.locals - node_modules
这很奇怪,不能正常工作,有时可以登录,有时不能。我认为这是异步调用的问题,但无法确定具体位置。 - Gaurav Agrawal
2个回答

2
在你的app.js文件中,你需要在初始化passport后调用const passportConfig = require('./config/passport');,就像这样:

最初的回答

app.use(passport.initialize());
app.use(passport.session());
// place it here
const passportConfig = require('./config/passport')(passport);

另外,您需要调整密码配置文件(./config/passport),以便在模块加载时它可以获取您在 app.js 中实例化的passport 实例,并且随后使用它而不是通过 require('passport') 加载新的passport 实例 - 在我看来这会使事情变得混乱; 不过我没有测试过。 :) 最初的回答。

0

你能分享一下关于passport.js LocalStrategy中这段代码所涉及到的用户对象示例吗?

user.comparePassword(password, (err, isMatch) => {....});

看起来 serializeuser 函数中没有 user.id 可用,这可能是无法在会话中设置 user.id 并且无法进一步反序列化用户而不在 req 中设置用户对象的原因。

passport.serializeUser((user, done) => {
  done(null, user.id);
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接