繁体   English   中英

使用 Passport 进行 Node.js 身份验证:如果缺少字段,如何闪烁消息?

[英]Node.js Authentication with Passport: How to flash a message if a field is missing?

我正在使用passport.js,如果我的表单字段为空,我想显示一条消息。 但我不知道该怎么做,因为如果缺少这些,passport 不会触发策略回调。 我真的希望这个用例更清楚,我不想修改passport。 我觉得有办法做到这一点,但我不知道在哪里! 我尝试使用路由的回调( app.post ),但它似乎并没有像我尝试的那样工作。

这是身份验证函数原型:

Strategy.prototype.authenticate = function(req, options) {
  options = options || {};
  var username = lookup(req.body, this._usernameField) || lookup(req.query, this._usernameField);
  var password = lookup(req.body, this._passwordField) || lookup(req.query, this._passwordField);
  // here is my problem
  if (!username || !password) {
    return this.fail({ message: options.badRequestMessage || 'Missing credentials' }, 400);
  }

  var self = this;

  function verified(err, user, info) {
    if (err) { return self.error(err); }
    if (!user) { return self.fail(info); }
    self.success(user, info);
  }

  try {
    if (self._passReqToCallback) {
      this._verify(req, username, password, verified);
    } else {
      this._verify(username, password, verified);
    }
  } catch (ex) {
    return self.error(ex);
  }
};

这是我的策略:

 passport.use('local-login', new LocalStrategy({
        usernameField : 'email',
        passwordField : 'password',
        passReqToCallback : true 
    },
    function(req, email, password, done) { 
        // ...
        console.log("Hello");
        User.findOne({ 'local.email' :  email }, function(err, user) {
            if (err)
                return done(err);

            // if no user is found, return the message
            if (!user)
                return done(null, false, req.flash('loginMessage', 'Pas d\'utilisateur avec ce login.')); // req.flash is the way to set flashdata using connect-flash

            // if the user is found but the password is wrong
            if (!user.validPassword(password))
                return done(null, false, req.flash('loginMessage', 'Oops! Mauvais password.')); // create the loginMessage and save it to session as flashdata

            // all is well, return successful user
            return done(null, user);
        });

    }));

最后我的路线:

app.get('/login', function(req, res) {

    // render the page and pass in any flash data if it exists
    res.render('login', { title: "Connexion", message: req.flash('loginMessage') }); 
});

// process the login form
    app.post('/login', passport.authenticate('local-login', {
        successRedirect : '/profile', // redirect to the secure profile section
        failureRedirect : '/login', // redirect back to the signup page if there is an error
        failureFlash : true // allow flash messages
    }, function(err, user, info) {
         // Was trying this callback, does'nt work, post callback maybe ?
         console.log("Hello");
    }));

您不应在验证回调中调用req.flash 相反,您应该返回一条消息,如文档中所示 failureFlash: true时,Passport 会将消息返回给 flash 消息:

failureFlash选项设置为true指示 Passport 使用策略的 verify 回调(如果有)给出的消息闪烁错误消息。

您修改后的验证回调:

passport.use('local-login', new LocalStrategy({...},
  function(email, password, done) { 
    User.findOne({ 'local.email' :  email }, function(err, user) {
      if (err)
        return done(err);
      if (!user)
        return done(null, false, {message: 'Pas d\'utilisateur avec ce login.'});
      if (!user.validPassword(password))
        return done(null, false, {message: 'Oops! Mauvais password.'});
      return done(null, user);
    });
  }));

和路线:

app.get('/login', function(req, res) {
  console.log(req.flash('error'));
  res.send();
});

app.post('/login', passport.authenticate('local-login', {
  successRedirect : '/profile',
  failureRedirect : '/login',
  failureFlash : true
}));

编辑:

这是一个完整的示例: https : //gist.github.com/vessel/9e23ff1810089bed4426

编辑:

这确实不能回答最初的问题,即我使用的是passport.js,如果我的表单字段为空,我想闪现一条消息 如果表单字段为空, passport-local策略只会执行fail ,因此应该在身份验证中间件之前检查它们并将 flash 消息设置在护照之外。

这是一个老问题,但我很难找到答案。 希望这对其他人有帮助。


我认为文档在使用connect-flash时有点不完整。 他们说:

注意:使用 flash 消息需要 req.flash() 函数。 Express 2.x 提供了此功能,但它已从 Express 3.x 中删除。 使用 Express 3.x 时,建议使用 connect-flash 中间件来提供此功能。

然而,没有提到在done()回调中使用 req.flash。 根据scotch.io 教程,您实际上应该在回调中直接调用req.flash() 这个对我有用。

// In your strategy
...
if (user) {
    return done( null, false, req.flash('loginMessage','Pas d\'utilisateur avec ce login.') );
...

您当然需要使用passReqToCallback 还要确保failureFlash设置为true OP 已经正确执行了这些操作。

现在您可以检查路由中的 flash 消息。 请注意, connect-flash发送一组消息 如果他的模板需要一个字符串,那可能是 OP 的问题。

// In your routes
app.get('/login', function(req, res) {

    // Test flash messages in the console
    console.log( req.flash('loginMessage') ); // This returns an array
    console.log( req.flash('loginMessage')[0] ); // This returns a string

    // render the page and pass in any flash data if it exists
    res.render('login', {
        title: "Connexion",
        message: req.flash('loginMessage')[0] // Don't forget the index! 
    });

});

如果页面上可能有多个登录消息,请传递整个req.flash('loginMessage')数组并在模板中遍历它。 下面是一个使用nunjucks的例子。


专家提示:

如果你有很多带有 flash 消息的路由,你总是可以在中间件路由中将它们设置为res.locals 这不会干扰其他本地人,例如title 这是我的实现,使用bootstrap alerts

在我的策略中:

...
if (!user){
    return done( null, false, req.flash('danger','No account exists for that email.') );
}
...

在我的 routes.js 中:

// Set flash messages
router.get('*', function(req,res,next){
    res.locals.successes = req.flash('success');
    res.locals.dangers = req.flash('danger');
    res.locals.warnings = req.flash('warning');
    next();
});

// Login route
app.get('/login', function(req, res) {
    res.render('login', { title: 'Login'}); 
});

在我的修女基本模板中:

<!--Messages-->
{% for danger in dangers %}
    <div class='header alert alert-danger alert-dismissible'>
        <strong><i class="fa fa-exclamation-circle"></i> ERROR:</strong> {{ danger | safe }}
        <a href="#" class='close' data-dismiss="alert" aria-label="close"><i class='fa fa-times'></i></a> 
    </div>
{% endfor %}
{% for warning in warnings %}
    <div class='header alert alert-warning alert-dismissible'>
        <strong><i class="fa fa-check-circle"></i> Warning:</strong> {{ warning | safe }}
        <a href="#" class='close' data-dismiss="alert" aria-label="close"><i class='fa fa-times'></i></a> 
    </div>
{% endfor %}
{% for success in successes %}
    <div class='header alert alert-success alert-dismissible'>
        <strong><i class="fa fa-check-circle"></i> Success!</strong> {{ success | safe }}
        <a href="#" class='close' data-dismiss="alert" aria-label="close"><i class='fa fa-times'></i></a> 
    </div>
{% endfor %}

您需要设置badRequestMessage并设置failureFlash: true

像这样:

passport.authenticate('login', {
    successRedirect : '/',
    failureRedirect : '/login',
    badRequestMessage : 'Missing username or password.',
    failureFlash: true
})

经过几个月的断断续续尝试使故障闪存工作,我终于找到了一个不使用故障闪存功能的解决方案。 我基本上创建了一个新路由并发送了 flash 消息。

app.post('/login',
  passport.authenticate('local', {failureRedirect: "/loginfailed"}),
  function(req, res) {
    if (!req.user.isActive){
      req.flash("success","Your account needs to be verified. Please check your email to verify your account");
      req.logout();
      res.redirect("back")
    }else{
      res.redirect("/");
    }
  });

  //Route to login page if user failed to login. I created this to allow flash messages and not interfere with regular login route
  app.get("/loginfailed", function(req, res){
    if (!req.user){
      req.flash("success", "Username or password is incorrect.");
      res.redirect("/login");
    }
  });

我有同样的问题,我解决了它。
您的成功消息和失败消息变量必须与通行证 JS 使用的任何内容相匹配。 所以玩了一圈之后,我意识到passport JS使用变量success来显示success flash和error来显示fail flash。

所以首先,你可以在 app.js 中创建一个像这样的超级全局变量:

app.use(function(req, res, next) {
    res.locals.error = req.flash("error");
    res.locals.success = req.flash("success");
    next();
});

然后在您的寺庙中使用这些变量。 我正在使用 ejs,所以它看起来像这样:

<%if(error && error.length > 0){%>
    <div class="alert alert-danger"><%=error%></div>
<%}%>
    <%if(success && success.length > 0){%>
    <div class="alert alert-success"><%=success%></div>
<%}%>

最后你的护照JS代码应该是这样的:

router.post("/login",passport.authenticate("local", {
    successFlash : "Hey, Welcome back",
    successRedirect : "/mountains",
    failureFlash : true,
    failureRedirect :"/login"
    }), function(req, res){
});

我的解决方案

app.js 代码:

const flash = require('connect-flash');
app.use(flash());
require('./src/config/passport.js')(app);

local.strategy.js 代码

const passport = require('passport');
const { Strategy } = require('passport-local');
const userModel = require('./../../../models/userModel');

module.exports = function localStrategy() {
passport.use(new Strategy(
    {
        usernameField: "username",
        passwordField: "password"
    }, (username, password, done) => {
        userModel.findOne({ username }, (err, user) => {
            if (err) {
                res.send(err);
            }
            if (user && (user.username == username && user.password == password)) {
                done(null, user, { message: "Success" });
            } else {
                done(null, false, { message: "Invalid credentials!" });
            }
        });
      }
       ));
  }

authController.js 代码

function signIn(req, res) {
    res.render('signin', {
        nav,
        title: "Sign in",
        message: req.flash()
    });
};

authRouter.js 代码

authRouter.route('/signin').get(signIn).post(passport.authenticate('local', {
    successRedirect: '/admin',
    failureRedirect: '/auth/signin',
    failureFlash: true
}));

signin.js 模板代码(我的视图引擎是 ejs)

<% if (message) { %>
  <p style="color: red;" class="text-center"><%= message.error %></p>
<% } %>

当身份验证所需的字段丢失时, passport.authenticate将不会触发OP 指出的策略回调
这必须通过使用info参数在身份验证函数中的自定义回调(向下滚动页面)中处理。
如果是这样的 OP 代码:

app.post('/login', function (req, res, next) { 
    passport.authenticate('local-login',
    {
      successRedirect: '/profile',
      failureRedirect: '/login',
      failureFlash: true,
    },
    function (error, user, info) {
      //This will print: 'Missing credentials'
      console.log(info.message);
      //So then OP could do something like 
      req.flash(info.message);
      //or in my case I didn't use flash and just did 
      if (info)
        res.status(400).send(info.message);
      ...
    })(req, res, next);
  });

我知道这个问题很老,但我自己偶然发现了这个问题,我发现仍然没有公认的答案。 此外,我认为所有的答案都误解了 OP 的实际要求 - 一种访问badRequestMessage
PassportJS 文档也不是很有帮助:

如果身份验证失败,用户将被设置为 false。 如果发生异常,将设置 err。 将传递一个可选的 info 参数,其中包含策略的验证回调提供的其他详细信息。

这实际上意味着info参数可以作为您的策略中的第三个参数传递,如下所示: done(error,user,info) ,但没有提到在缺少凭据的情况下默认使用此参数。 总的来说,我认为 PassportJS 文档可以做一些大修,因为它们缺乏细节和指向不存在的示例的链接

这个答案帮助我理解在info参数中传递了缺少的凭据消息。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM