使用 Express Passport(node.js)时的错误处理

76

我查看了关于node中错误处理应该如何工作的问题,链接为Error handling principles for Node.js + Express.js applications?,但是当passport出现身份验证失败时,我不确定它在做什么。我有以下LocalStrategy:

passport.use(new LocalStrategy({ usernameField: 'email', passwordField: 'password' },
  function(email, password, next) {
 
    User.find({email: UemOrUnm}, function(err, user){
      if (err) { console.log('Error > some err'); return next(err); }
      if (!user) { console.log('Error > no user'); return next('Incorrect login or password'); } 

      if (password != user.password) {
        return next(Incorrect login or password);
      }
      return next(null, user);
    });
  }
));

在我看到控制台输出 'Error > some err' 后,就没有其他事情发生了。我认为它应该带着错误参数继续执行下一个路径,但似乎并没有这样做。发生了什么?

6个回答

176

策略实现与passport.authenticate协同工作,既可以对请求进行身份验证,又可以处理成功/失败。

假设您正在使用此路由(传递电子邮件地址和密码):

app.post('/login', passport.authenticate('local', {
  successRedirect: '/loggedin',
  failureRedirect: '/login', // see text
  failureFlash: true // optional, see text as well
});

这将调用策略中的代码,有三种情况会发生:
  1. 尝试获取用户信息时发生内部错误(例如数据库连接丢失);此错误将被传递:next(err);这将由Express处理并生成HTTP 500响应;
  2. 提供的凭据无效(没有使用提供的电子邮件地址找到用户,或密码不匹配);在这种情况下,您不会生成错误,而是将false作为用户对象传递:next(null, false);这将触发failureRedirect(如果未定义,则将生成HTTP 401未经授权的响应);
  3. 一切都检查出来了,您拥有一个有效的用户对象,因此将其传递:next(null, user);这将触发successRedirect

如果认证无效(但不是内部错误),则可以在回调中传递额外的消息:

next(null, false, { message : 'invalid e-mail address or password' });

如果您使用了failureFlash并安装了connect-flash中间件,则提供的消息将被存储在会话中,并且可以轻松访问,例如在模板中使用。 编辑:您也可以完全自己处理身份验证过程的结果(而不是Passport发送重定向或401):
app.post('/login', function(req, res, next) {
  passport.authenticate('local', function(err, user, info) {
    if (err) {
      return next(err); // will generate a 500 error
    }
    // Generate a JSON response reflecting authentication status
    if (! user) {
      return res.send({ success : false, message : 'authentication failed' });
    }
    // ***********************************************************************
    // "Note that when using a custom callback, it becomes the application's
    // responsibility to establish a session (by calling req.login()) and send
    // a response."
    // Source: http://passportjs.org/docs
    // ***********************************************************************
    req.login(user, loginErr => {
      if (loginErr) {
        return next(loginErr);
      }
      return res.send({ success : true, message : 'authentication succeeded' });
    });      
  })(req, res, next);
});

3
我使用了您的自定义身份验证代码,但是当我在以后的请求中调用req.isAuthenticated时,返回的值是false。 - thebiglebowski11
@pka2012 我假设最初的身份验证已经成功了,是吗?如果是这样,请确保Express的会话处理已正确配置。 - robertklep
1
@thebiglebowski11(我想)你仍然需要调用req.logIn()。这个答案演示了它https://dev59.com/1oPba4cB1Zd3GeqPvrBp#26032067 - anthonygore
点赞!这对Google策略有效吗?我想自己处理重定向和用户登录的Google授权。如果我使用了这个回调,我该如何管理重定向? - PirateApp

22

基督徒所说的是您需要添加该函数。

req.login(user, function(err){
  if(err){
    return next(err);
  }
  return res.send({success:true});
});

所以整个路线将是:

app.post('/login', function(req, res, next) {
  passport.authenticate('local', function(err, user, info) {
    if (err) {
      return next(err); // will generate a 500 error
    }
    // Generate a JSON response reflecting authentication status
    if (! user) {
      return res.send(401,{ success : false, message : 'authentication failed' });
    }
    req.login(user, function(err){
      if(err){
        return next(err);
      }
      return res.send({ success : true, message : 'authentication succeeded' });        
    });
  })(req, res, next);
});

来源: http://passportjs.org/guide/login/


3
你需要添加 req.logIn(function(err){}); ,并在回调函数内执行成功重定向

你能否请澄清一下你的回答? - dgilperez

0

这是在失败路由中执行 console.log(req) 后得到的结果。

const localStrategy = new LocalStrategy({ usernameField: "email" }, verifyUser);

passport.use(localStrategy);

const authenticateWithCredentials = passport.authenticate("local", {
  failureRedirect: "/api/auth/login-fail",
  failureMessage: true,
});

验证方法从数据库中查找您的用户,并在发现任何问题时向回调函数抛出错误

const verifyUser = async (email, password, cb) => {
  const user = await User.findOne({ email });
  if (!user) return cb(null, false, { message: "email/password incorrect!" });

  const isMatched = await user.comparePassword(password);
  if (!isMatched)
    return cb(null, false, { message: "email/password incorrect!" });

  cb(null, {
    id: user._id,
    email,
    name: user.name,
  });
};

现在设置你的路由

router.post("/sign-in", authenticateWithCredentials,(req, res) => {
  res.json({user: req.user})
});

router.get("/login-fail", (req, res) => {
  let message = "Invalid login request!";

  // if you are using typescript cast the sessionStore to any 
  const sessions = req.sessionStore.sessions || {};
  for (let key in sessions) {
    const messages = JSON.parse(sessions[key])?.messages;
    if (messages.length) {
      message = messages[0];
      break;
    }
  }

  res.status(401).json({ error: message });
});

0

我发现这个帖子非常有用!

https://github.com/jaredhanson/passport-local/issues/2

您可以使用此功能返回错误并以表单形式呈现。

app.post('/login',
  passport.authenticate('local', { successRedirect: '/home', failWithError: true }),
  function(err, req, res, next) {
    // handle error
    return res.render('login-form');
  }
);

0

一些时间过去了,现在最正确的代码将是:

  passport.authenticate('local', (err, user, info) => {
    if (err) {
      return next(err); // will generate a 500 error
    }
    // Generate a JSON response reflecting authentication status
    if (!user) {
      return res.status(401).send({ error: 'Authentication failed' });
    }
    req.login(user, (err) => {
      if (err) {
        return next(err);
      }
      return res.status(202).send({ error: 'Authentication succeeded' });    
    });
});

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