passport.js Passport.initialize() 中间件未使用。

117

我正在使用express + mongoose的node,并尝试在restful api中使用passport.js。
身份验证成功后,我不断收到以下异常信息(我在浏览器上看到回调URL):

/Users/naorye/dev/naorye/myproj/node_modules/mongoose/lib/utils.js:419
        throw err;
              ^
Error: passport.initialize() middleware not in use
    at IncomingMessage.req.login.req.logIn (/Users/naorye/dev/naorye/myproj/node_modules/passport/lib/passport/http/request.js:30:30)
    at Context.module.exports.delegate.success (/Users/naorye/dev/naorye/myproj/node_modules/passport/lib/passport/middleware/authenticate.js:194:13)
    at Context.actions.success (/Users/naorye/dev/naorye/myproj/node_modules/passport/lib/passport/context/http/actions.js:21:25)
    at verified (/Users/naorye/dev/naorye/myproj/node_modules/passport-facebook/node_modules/passport-oauth/lib/passport-oauth/strategies/oauth2.js:133:18)
    at Promise.module.exports.passport.use.GitHubStrategy.clientID (/Users/naorye/dev/naorye/myproj/config/passport.js:91:24)
    at Promise.onResolve (/Users/naorye/dev/naorye/myproj/node_modules/mongoose/node_modules/mpromise/lib/promise.js:162:8)
    at Promise.EventEmitter.emit (events.js:96:17)
    at Promise.emit (/Users/naorye/dev/naorye/myproj/node_modules/mongoose/node_modules/mpromise/lib/promise.js:79:38)
    at Promise.fulfill (/Users/naorye/dev/naorye/myproj/node_modules/mongoose/node_modules/mpromise/lib/promise.js:92:20)
    at /Users/naorye/dev/naorye/myproj/node_modules/mongoose/lib/query.js:1822:13

我读到过应该在 app.use(app.router); 之前放置 app.use(passport.initialize());app.use(passport.session());。我按照这个方法进行了操作。这是我的注册中间件的 express.js:

var express = require('express'),
    mongoStore = require('connect-mongo')(express),
    flash = require('connect-flash'),
    helpers = require('view-helpers');

module.exports = function (app, config, passport) {
    app.set('showStackError', true);
    // should be placed before express.static
    app.use(express.compress({
        filter: function (req, res) {
            return /json|text|javascript|css/.test(res.getHeader('Content-Type'));
        },
        level: 9
    }));
    app.use(express.favicon());
    app.use(express.static(config.root + '/public'));

    app.use(express.logger('dev'));

    // set views path, template engine and default layout
    app.set('views', config.root + '/app/views');
    app.set('view engine', 'jade');

    app.configure(function () {
        // use passport session
        app.use(passport.initialize());
        app.use(passport.session());

        // dynamic helpers
        app.use(helpers(config.app.name));

        // cookieParser should be above session
        app.use(express.cookieParser());

        // bodyParser should be above methodOverride
        app.use(express.bodyParser());
        app.use(express.methodOverride());

        // express/mongo session storage
        app.use(express.session({
            secret: 'linkit',
            store: new mongoStore({
                url: config.db,
                collection : 'sessions'
            })
        }));

        // connect flash for flash messages
        app.use(flash());

        // routes should be at the last
        app.use(app.router);

        // assume "not found" in the error msgs
        // is a 404. this is somewhat silly, but
        // valid, you can do whatever you like, set
        // properties, use instanceof etc.
        app.use(function(err, req, res, next){
            // treat as 404
            if (~err.message.indexOf('not found')) {
                return next();
            }

            // log it
            console.error(err.stack);

            // error page
            res.status(500).render('500', { error: err.stack });
        });

        // assume 404 since no middleware responded
        app.use(function(req, res, next){
            res.status(404).render('404', {
                url: req.originalUrl,
                error: 'Not found'
            });
        });
    });
};

有什么问题吗?

更新 根据@Peter Lyons的建议,我已将配置顺序更改为以下顺序,但仍然遇到了相同的错误:

var express = require('express'),
    mongoStore = require('connect-mongo')(express),
    flash = require('connect-flash'),
    helpers = require('view-helpers');

module.exports = function (app, config, passport) {
    app.set('showStackError', true);
    // should be placed before express.static
    app.use(express.compress({
        filter: function (req, res) {
            return /json|text|javascript|css/.test(res.getHeader('Content-Type'));
        },
        level: 9
    }));
    app.use(express.favicon());
    app.use(express.static(config.root + '/public'));

    app.use(express.logger('dev'));

    // set views path, template engine and default layout
    app.set('views', config.root + '/app/views');
    app.set('view engine', 'jade');

    app.configure(function () {

        // dynamic helpers
        app.use(helpers(config.app.name));

        // cookieParser should be above session
        app.use(express.cookieParser());

        // bodyParser should be above methodOverride
        app.use(express.bodyParser());
        app.use(express.methodOverride());

        // express/mongo session storage
        app.use(express.session({
            secret: 'linkit',
            store: new mongoStore({
                url: config.db,
                collection : 'sessions'
            })
        }));

        // connect flash for flash messages
        app.use(flash());

        // use passport session
        app.use(passport.initialize());
        app.use(passport.session());

        // routes should be at the last
        app.use(app.router);

        // assume "not found" in the error msgs
        // is a 404. this is somewhat silly, but
        // valid, you can do whatever you like, set
        // properties, use instanceof etc.
        app.use(function(err, req, res, next){
            // treat as 404
            if (~err.message.indexOf('not found')) {
                return next();
            }

            // log it
            console.error(err.stack);

            // error page
            res.status(500).render('500', { error: err.stack });
        });

        // assume 404 since no middleware responded
        app.use(function(req, res, next){
            res.status(404).render('404', {
                url: req.originalUrl,
                error: 'Not found'
            });
        });
    });
};

Express 4.x 版本不支持某些方法。请参见 https://github.com/strongloop/express/wiki/Migrating-from-3.x-to-4.x。 - miksiii
8个回答

229

跟随这个例子,避免进入Express容易陷入的未按顺序的中间件地狱。直接从文档中获取。注意你的代码可能与此略有不同。

var app = express();
app.use(require('serve-static')(__dirname + '/../../public'));
app.use(require('cookie-parser')());
app.use(require('body-parser').urlencoded({ extended: true }));
app.use(require('express-session')({
  secret: 'keyboard cat',
  resave: true,
  saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());

文档

  1. cookieParser
  2. session
  3. passport.initialize
  4. passport.session
  5. app.router

  1. passport.initialize
  2. passport.session
  3. cookieParser
  4. session
  5. app.router

我已经按照你的建议进行了更改,但仍然出现这个错误。我已经更新了我的问题,并附上了新的express.js文件。 - Naor
7
那么你这里的代码不是你的顶级代码。在程序的早期,你是否调用了app.getapp.post等函数?这些函数会导致路由器比你预期的早添加到堆栈中。请展示从调用express()函数获取你的app对象开始的所有相关代码。这是我的第二个猜测。 - Peter Lyons
3
我注意到在调用passport初始化之后,app.use(app.router);被调用,但我在调用express的配置之前就已经调用了require('./config/routes')(app, passport, auth);。交换这两行代码解决了问题。谢谢! - Naor
1
这对我有用!但是为什么中间件必须按那样的顺序呢? - Anthony To
3
设计上考虑到了先决条件的完成,因此您可以信赖它。如果cookieParser尚未解析cookie,则会话将无法工作。 - Peter Lyons
解决了我的问题。对于其他使用Express 4+的人,请记住一些使用的软件包不再与Express一起提供,必须单独安装。错误消息相当好并且有解释性,所以这只是一个小麻烦。 - Alan Thomas

13

在我的情况下(相同的错误信息),我忘记完全添加护照初始化:

app.configure(function () {
    ...
    app.use(passport.initialize());
    app.use(passport.session());
});

更新:仅适用于 Express 版本 3,版本 4 不再支持 app.configure()。


1
App.configure不能再使用了。https://github.com/strongloop/express/wiki/Migrating-from-3.x-to-4.x#removed-in-express-4.。他们应该更新passport文档,对吧? - jack blank

9
在我的情况下,错误是因为我尝试在不将 this 绑定到 req 的情况下 promisify req.login,因此当该函数被调用时找不到 passport 设置。
解决方案是在使用 Node v8 时,在传递给 promisify 之前绑定 req.login.bind(req)

这种“作用域问题”通常会在使用参数解构时出现,例如 function({ login })req 作为第一个参数传递。你提供的解决方案对我很有帮助,谢谢。 - Manuel Di Iorio
没错,这就是 JavaScript 中 this 的工作方式。如果你不将函数作为对象方法调用,那么 this 将会是 undefined(或者在浏览器中是 window)。 - Jiayi Hu
对于阅读此答案但不理解的任何人,这里有一个提示...如果你研究Function.prototype.callFunction.prototype.apply、Javascript中this的工作原理以及原型继承的原则,你将在此过程中晋升为Javascript大师 :) - Stijn de Witt
谢谢,我希望它像这样简单:util.promisify(req.login.bind(req)); - Julian H. Lam

4

对我有帮助的是将路由配置放在cookies配置之后:

// init Cookies:
app.use(
    cookieSession({
        maxAge: 30 * 24 * 60 * 60 * 1000,
        keys: [keys.cookieKey]
    })
);
app.use(passport.initialize());
app.use(passport.session());

// init routes
const authRoutes = require("./routes/authRoutes")(app);

你有任何想法为什么在配置工作之后会初始化路由吗? - Ishu
这解决了我的问题。我把所有的routes.use调用都放在与passport相关的内容之后。 - Nick Van Brunt

3

Peter Lyons的回答帮助了我解决了问题,但是我用了一种略微不同的方法解决了它。

app.use(
  cookieSession({
    maxAge: 30 * 24 * 60 * 60 * 1000,
    keys: [keys.cookieKey],
  }),
);
app.use(passport.initialize());
app.use(passport.session());

请查看我的GitHub存储库,以获取完整的代码而不仅仅是此处的代码片段。

2

在我的情况下(同样的错误信息),我正在开发一个自定义策略,而且我不需要使用会话。我只是忘记在我的路由authenticate中间件中添加session: false

  app.post('/api/public/auth/google-token',
    passport.authenticate('google-token', {
      session: false
    }),
    function (req: any, res) {
      res.send("hello");
    }
  );

1

app.use(passport.initialize())中间件放在app.router中间件前面,它就能像魔法一样运行。


0

你所需要做的就是把这段代码放在你正在使用的路由器之前,像这样

app.use('/users', usersRouter);//this is the router  I am using

//and this is the code you have to copy

app.use(passport.initialize());
app.use(passport.session());


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