【问题标题】:Passport Local Strategy- Custom Callback for Single Page AppPassport 本地策略 - 单页应用程序的自定义回调
【发布时间】:2025-11-26 12:50:01
【问题描述】:

我是 Node 新手,我正在构建一个单页 Web 应用程序。我正在使用 Express 和 Mongoose,尝试设置像 these docs. 这样的登录调用。我可能完全搞错了,所以很抱歉。

首先,我正在收听登录帖子。这是我想根据响应是否经过身份验证向响应添加自定义数据的地方。 The documentation 我提到了 res 作为参数传入,但我看不到访问它的方法。另外,第一个参数应该是“err”(通常为空)吗?我的承诺中的.catch 语句需要调用done(),否则请求不会完成。

app.post('/login',passport.authenticate('local-signin', 
    function(req, res) {
        // wasn't sure what I should be doing here
    }
  ));

护照设置:

passport.use('local-signin', new LocalStrategy(
{passReqToCallback : true}, 
  function(req, username, password, done) {
    funct.localAuth(username, password)
    .then(function (user) {
      if (user) {
        console.log("LOGGED IN AS: " + user.username);
        done(null, user);
      }
      if (!user) {
        done(null, false);
      }
    })
    .catch(function (err){
      console.error(err.message);
      done(err);
    });
  }
));

以及身份验证处理程序:

exports.localAuth = function (username, password) {

   return db.User.findOne({ username: username }).exec()
   .then(function (result){
      if(result){
         var hash = result._doc.passwordHash;

         if(!hash){
            console.warn("User " + username + " has no password set");
            return null;
         }
         console.log(bcrypt.compareSync(password, hash));
         if (bcrypt.compareSync(password, hash)) {
            return result;
         } else {
            return null;
         }
      } else {
         return null;
      }
   },
      function(reason){
         console.error("Failed to login: " + reason);
      }
   );
};

【问题讨论】:

  • 打印什么:console.log("LOGGED IN AS: " + user.username);?你还说// res isn't res! res is the 。 Res 是什么?
  • 这一行看起来是你的问题:return db.User.findOne({ username: username }).exec() 这不会返回你认为它所做的,因为findOne 返回一个承诺,一旦它被执行,节点就已经开始下一个工作并且在下一个事件循环(下一个滴答声)之前,不会回来查看该承诺已解决的问题。

标签: node.js express passport.js passport-local


【解决方案1】:

再次,我认为您面临的主要问题是:

 funct.localAuth(username, password)
.then(function (user) {

return db.User.findOne({ username: username }).exec()
.then(function (result){

问题是 Node 在它有机会被返回之前已经进入了下一个事情。在此之后注销某些内容,您会看到它在 findOne 解析用户之前已记录。

function logUser() {
  db.User.findOne({ username: username }).exec()
    .then(function (user) {

      console.log('this will log second', user)
    })

  console.log('this will log first')
}

以下是我在项目中设置护照的方式:

auth/index.js

passport.use(new LocalStrategy({ passReqToCallback : true }, function (req, username, password, done) {

  db.User.findOne({ username: username }).exec()
    .then(function (user) {

      // It's a security thing to not let the user (potential hacker) to know whether a user exists
      // in the system or not, or if the email was wrong, or just the password, send them a generic error.
      if (!user)
        return done('Could not log you in with that email and password combination.', false)

      account.validatePassword(password)
        .then(function (result) {
          console.log(result) // The bcrypt result
          done(null, user)
        }, function (err) {
          console.error('Could not validate password', err)
          done(err, false)
        })

    }, function (err) {
        console.error('Could not find user', err)
    })

}))


module.exports = passport

现在您可以在其他地方(例如中间件)使用 auth 模块以及添加其他策略(例如承载)。

models/user.js

userSchema.methods.validatePassword = function (password) {
  var user = this

  // This is how you properly return a promise.
  // Notice I'm either resolving or rejecting, not returning the result inside the function (I'm only using `return` so it exits the function early.)
  return new Promise(function (resolve, reject) {
    bcrypt.compare(password, user.passwordHash, function (err, result) {
      if (err)
        return reject(err)

      if (!result)
        return reject('Could not log you in with that email and password combination.')

      resolve(result)
    })
  })
}

中间件/local-auth.js

var passport = require('../auth')

module.exports = passport.authenticate('local-auth')

路由器/路由/users.js

var localAuth = require('../../middleware/local-auth')

router.post('/login', localAuth, login)
// Other routes...

return router


function login(req, res) {
  // You have have a req.user
  res.send(req.user)
}

router/index.js(这可能有点过火,但我想提供一个真实的解决方案)

module.exports = function (app) {
  app.use('/api/users', require('./routes/users'))
  // Other endpoints...
}

server.js

require('./middleware/express')(app)
require('./router')(app)

至于done()业务:

done() 回调接受两个参数:错误,用户。 在 Passport 的某个地方,它接收您使用以下语法传递的参数:

function (err, user) { }

这是社区采用的 Node.js 标准,用于为回调带来秩序,因为在 Promise 或生成器之前,回调是“事物”,人们有时将其作为最后一个参数,甚至放在中间的某个位置。现在总是第一个。您可能会看到它也称为next(),它是同一回事。如果需要,将其更改为next()moveItAlong(),它将像以前一样执行。只需在您的代码中使用next(),以便对其他人有意义。

因此,当您传递done(null, user) 时,它仅表示没有错误,而在 Passport 中,当回调处理程序检查错误时,什么都没有,它会继续。如果您不调用回调 (done()),则不会发生任何事情。 Passport 永远不会收到错误或用户,因为您从未调用过回调;这就是为什么您还需要在您的.catch() 中调用done()

【讨论】:

    最近更新 更多