Javascript 使用 Passport 进行 Node.js 身份验证:如果缺少字段,如何闪烁消息?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26403853/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Node.js Authentication with Passport: How to flash a message if a field is missing?
提问by Mael Jarnole
I am using passport.js and I'd like to flash a message if the fields of my form are empty. But I don't know how to do it since passport doesn't trigger the strategy callback if those are missing. I really want this use case to be more clear, and I don't want to modify passport. I feel like there is a way to do so but I don't know where! I've tried to use the callback of the route (app.post) but it doesn't seem to work the way I tried.
我正在使用passport.js,如果我的表单字段为空,我想显示一条消息。但是我不知道该怎么做,因为如果缺少这些,passport 不会触发策略回调。我真的希望这个用例更清楚,我不想修改passport。我觉得有办法做到这一点,但我不知道在哪里!我曾尝试使用路由 ( app.post)的回调,但它似乎不像我尝试的那样工作。
Here is the authenticate function prototype:
这是身份验证函数原型:
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);
}
};
Here is my strategy:
这是我的策略:
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);
});
}));
And finally my route:
最后我的路线:
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");
}));
回答by vesse
You should not call req.flashin your verify callback. Instead you should return a message as shown in the documentation. Passport will put the message returned to flash message when failureFlash: true:
您不应调用req.flash验证回调。相反,您应该返回一条消息,如文档中所示。Passport 会在以下情况下将消息返回给 flash 消息failureFlash: true:
Setting the
failureFlashoption totrueinstructs Passport to flash an error message using the message given by the strategy's verify callback, if any.
将
failureFlash选项设置为true指示 Passport 使用策略的 verify callback 给出的消息(如果有)闪烁错误消息。
Your revised verify callback:
您修改后的验证回调:
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);
});
}));
And routes:
和路线:
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
}));
Edit:
编辑:
Here's a fully working example: https://gist.github.com/vesse/9e23ff1810089bed4426
这是一个完整的示例:https: //gist.github.com/vessel/9e23ff1810089bed4426
回答by Keith
It's an old question, but I had trouble finding an answer. Hopefully this helps others.
这是一个老问题,但我很难找到答案。希望这对其他人有帮助。
I think the documentationis a little incomplete when it comes to using connect-flash. They say:
我认为文档在使用connect-flash. 他们说:
Note: Using flash messages requires a req.flash() function. Express 2.x provided this functionality, however it was removed from Express 3.x. Use of connect-flash middleware is recommended to provide this functionality when using Express 3.x.
注意:使用 flash 消息需要 req.flash() 函数。Express 2.x 提供了此功能,但它已从 Express 3.x 中删除。使用 Express 3.x 时,建议使用 connect-flash 中间件来提供此功能。
Yet, there's no mention of using req.flash in the done()callback. Based on the scotch.io tutorial, you actually shouldcall req.flash()right there in the callback. It works for me.
然而,没有提到在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.') );
...
You will need to use passReqToCallbackof course. Also be sure failureFlashis set to true. OP is already doing these correctly.
你passReqToCallback当然需要使用。另外一定failureFlash要设置为true. OP 已经正确执行了这些操作。
Now you can check the flash message in the route. Note that connect-flashsends an array of messages. That could be OP's problem, if his template is expecting a string.
现在您可以检查路由中的 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!
});
});
If there's a chance of having multiple login messages on a page, pass the whole req.flash('loginMessage')array and iterate through it in your template. Below is an example using nunjucks.
如果页面上可能有多个登录消息,请传递整个req.flash('loginMessage')数组并在模板中遍历它。下面是一个使用nunjucks的例子。
Protip:
专家提示:
If you have many routes with flash messages, you can always set them to res.localsin a middleware route. This will not interfere with other locals, like title. Here is my implementation, using bootstrap alerts.
如果您有许多带有 flash 消息的路由,您可以随时将它们设置res.locals为中间件路由。这不会干扰其他本地人,例如title. 这是我的实现,使用bootstrap alerts。
In my strategy:
在我的策略中:
...
if (!user){
return done( null, false, req.flash('danger','No account exists for that email.') );
}
...
In my routes.js:
在我的 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'});
});
In my nunjucks base template:
在我的修女基本模板中:
<!--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 %}
回答by MrR0FL0LMA0MG
You need to set badRequestMessageand set failureFlash: true.
您需要设置badRequestMessage和设置failureFlash: true。
Like this:
像这样:
passport.authenticate('login', {
successRedirect : '/',
failureRedirect : '/login',
badRequestMessage : 'Missing username or password.',
failureFlash: true
})
回答by Joel
After months of on and off trying to get failure flash to work, i finally found a solution which doesnt use the failureFlash feature. I basically created a new route and sent the flash message.
经过几个月的断断续续尝试使故障闪存工作,我终于找到了一个不使用故障闪存功能的解决方案。我基本上创建了一个新路由并发送了 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");
}
});
回答by Dawid Stró?ak
When fields required for authentication are missing, passport.authenticatewill not trigger the Strategy callbackas OP points out.
This has to be handled inside the custom callback(scroll down page) in the authenticate function by using the infoparameter.
In case of the OP's code like so:
当缺少身份验证所需的字段时,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);
});
I know this question is old but I have stumbled upon this issue myself and I see that there is still no accepted answer. Furthermore I think that all the answers misinterpreted what the OP was actually asking for - a way to access the badRequestMessage.
PassportJS docs are not very helpful either:
我知道这个问题很老,但我自己偶然发现了这个问题,我发现仍然没有公认的答案。此外,我认为所有的答案都误解了 OP 的实际要求——一种访问badRequestMessage.
PassportJS 文档也不是很有帮助:
If authentication failed, user will be set to false. If an exception occurred, err will be set. An optional info argument will be passed, containing additional details provided by the strategy's verify callback.
如果身份验证失败,用户将被设置为 false。如果发生异常,将设置 err。将传递一个可选的 info 参数,其中包含策略的验证回调提供的其他详细信息。
What this actually means is that infoparameter can be passed as a third parameter from your strategy like so: done(error,user,info), but fails to mention that this parameter is used by default in case of missing credentials. Overall I think PassportJS docs could do with some overhaul as they lack detail and link to non-existent examples.
这实际上意味着该info参数可以作为您策略中的第三个参数传递,例如:done(error,user,info),但没有提到在缺少凭据的情况下默认使用此参数。总的来说,我认为 PassportJS 文档可以做一些大修,因为它们缺乏细节和指向不存在的示例的链接。
This answerhas helped me understand that the missing credentials message is passed in the infoparameter.
这个答案帮助我理解在info参数中传递了缺少的凭据消息。
回答by Nijat Aliyev
My solution
我的解决方案
app.js code:
app.js 代码:
const flash = require('connect-flash');
app.use(flash());
require('./src/config/passport.js')(app);
local.strategy.js code
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 code
authController.js 代码
function signIn(req, res) {
res.render('signin', {
nav,
title: "Sign in",
message: req.flash()
});
};
authRouter.js code
authRouter.js 代码
authRouter.route('/signin').get(signIn).post(passport.authenticate('local', {
successRedirect: '/admin',
failureRedirect: '/auth/signin',
failureFlash: true
}));
signin.js template code (my view engine is ejs)
signin.js 模板代码(我的视图引擎是 ejs)
<% if (message) { %>
<p style="color: red;" class="text-center"><%= message.error %></p>
<% } %>
回答by youssouf diarra
I had the same problem and I solved it.
Your success message and failure message variables have to match with whatever the passport JS is using. So after playing around, I realize that passport JS is using the variable successto display success flash and errorto display failure flash.
我有同样的问题,我解决了它。
您的成功消息和失败消息变量必须与通行证 JS 使用的任何内容相匹配。所以玩了一圈之后,我意识到passport JS使用变量success来显示success flash和error来显示fail flash。
So first, you can create a super global variable like this in your app.js:
所以首先,你可以在 app.js 中创建一个像这样的超级全局变量:
app.use(function(req, res, next) {
res.locals.error = req.flash("error");
res.locals.success = req.flash("success");
next();
});
Then use those variables in your temple. I am using ejs so it looks like this:
然后在您的寺庙中使用这些变量。我正在使用 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>
<%}%>
And finally your passport JS code should be like this:
最后你的护照JS代码应该是这样的:
router.post("/login",passport.authenticate("local", {
successFlash : "Hey, Welcome back",
successRedirect : "/mountains",
failureFlash : true,
failureRedirect :"/login"
}), function(req, res){
});

