Passport + NodeJs + Express 得到“req.user”未定义

Posted

技术标签:

【中文标题】Passport + NodeJs + Express 得到“req.user”未定义【英文标题】:Passport + NodeJs + Express getting "req.user" undefined 【发布时间】:2019-08-13 18:28:02 【问题描述】:

我面临一个会话问题,在成功通行证后获取 req.user 未定义。验证方法。

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

我使用了一个使用 mongo 的 hackathon 启动器,我试图改变一些东西以使用 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`);
  
;

【问题讨论】:

Node + Express + Passport: req.user Undefined的可能重复 伙计,它成功了.. 谢谢.. 你也能解释一下为什么需要这样做。我还添加了“ res.locals.user = user;”在重定向之前。这是添加它的正确方法。 很高兴它对你有用。这就是我在请求中传递我的用户对象的方式。更多解释在这里:app.locals 这很奇怪,不能正常工作,有时登录有时不登录。我认为这是某处的 asyc 调用问题..无法弄清楚在哪里 【参考方案1】:

在你的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 实例 - 我认为这会搞砸;虽然没有测试它。 :)

【讨论】:

【参考方案2】:

您能否分享在您的 passport.js LocalStrategy 中引用此代码的用户对象示例

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

看起来 user.id 在下面的 serializeuser 中不可用,这可能是它无法在会话中设置 user.id 并且无法反序列化用户进一步未在 req 中设置用户对象的原因

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

【讨论】:

以上是关于Passport + NodeJs + Express 得到“req.user”未定义的主要内容,如果未能解决你的问题,请参考以下文章

NestJS / NodeJS / Passport / JWT - 股票当前用户

Nodejs + Passport.js + Redis:如何在 Redis 中存储会话

NodeJs:错误401(未经授权)我正在使用passport-jwt

nodejs - passport.use 回调返回 dataValues 和 _previousDataValues 而不是普通对象

req.user.displayname 未定义 Nodejs + passport = google oauth

Passport + NodeJs + Express 得到“req.user”未定义