带有 Passport 的 Node.js 身份验证:如果缺少字段,如何闪烁消息?
Posted
技术标签:
【中文标题】带有 Passport 的 Node.js 身份验证:如果缺少字段,如何闪烁消息?【英文标题】:Node.js Authentication with Passport: How to flash a message if a field is missing? 【发布时间】:2014-12-11 18:54:18 【问题描述】:我正在使用 passport.js,如果我的表单字段为空,我想闪现一条消息。但我不知道该怎么做,因为如果护照丢失,护照不会触发策略回调。我真的希望这个用例更清楚,我不想修改护照。我觉得有办法做到这一点,但我不知道在哪里!我尝试使用路由的回调 (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");
));
【问题讨论】:
你的玉石或模板在哪里?你应该检查message
,如果它存在则显示
Precision :问题是当用户或密码错误时已经有一个闪烁消息,但当字段为空时没有。而且我无法做到这一点,因为策略中的回调在这种情况下不被护照调用(请参阅身份验证原型)。我不想破解护照密码,一定有办法..
return this.fail( message: options.badRequestMessage || 'Missing credentials' , 400);
【参考方案1】:
您不应在验证回调中调用req.flash
。相反,您应该返回消息as shown in the documentation。当failureFlash: true
:
将
failureFlash
选项设置为true
会指示Passport 使用策略的验证回调给出的消息(如果有)来闪烁错误消息。
您修改后的验证回调:
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/vesse/9e23ff1810089bed4426
编辑:
这确实不能回答原来的问题,即我正在使用 passport.js,如果我的表单字段为空,我想闪现一条消息。 passport-local
策略只会在表单字段为空时执行 fail
,因此应在身份验证中间件之前对其进行检查,并将 flash 消息设置在护照之外。
【讨论】:
呃。这并不能解决我的问题(空字段 cf op 和 op cmets),感谢您以任何一种方式做出回应。如果我按照您建议的方式修改代码,则未定义 flash。 那么还有其他问题。我添加了一个指向包含完整工作示例的要点的链接。 我最近遇到了这个问题。我查看了您的要点,发现了一些我错过的东西,这非常有效。谢谢您的帮助! @vesse 在我的情况下,console.log(flash('error'))
出于某种原因仅显示[Function]
。
问题是你应该使用 req.flash('error') 而不是 flash('error')。【参考方案2】:
这是一个老问题,但我很难找到答案。希望这对其他人有所帮助。
我认为the documentation 在使用connect-flash
时有点不完整。他们说:
注意:使用 flash 消息需要 req.flash() 函数。 Express 2.x 提供了此功能,但它已从 Express 3.x 中删除。使用 Express 3.x 时,建议使用 connect-flash 中间件来提供此功能。
然而,没有提到在done()
回调中使用 req.flash。基于scotch.io tutorial,您实际上应该在回调中调用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');
);
在我的 nunjucks 基础模板中:
<!--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 %
【讨论】:
【参考方案3】:你需要设置badRequestMessage
和设置failureFlash: true
。
像这样:
passport.authenticate('login',
successRedirect : '/',
failureRedirect : '/login',
badRequestMessage : 'Missing username or password.',
failureFlash: true
)
【讨论】:
【参考方案4】:经过几个月的断断续续尝试让故障闪存工作,我终于找到了一个不使用故障闪存功能的解决方案。我基本上创建了一条新路由并发送了 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");
);
【讨论】:
【参考方案5】:我遇到了同样的问题,我解决了。 您的成功消息和失败消息变量必须与护照 JS 使用的任何内容相匹配。所以玩了一圈,才发现passport JS是用变量success来显示成功闪现,error显示失败闪现。
首先,您可以在 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)
);
【讨论】:
【参考方案6】:我的解决方案
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>
<% %>
【讨论】:
【参考方案7】:当缺少身份验证所需的字段时,passport.authenticate
不会触发策略回调,正如 OP 指出的那样。
这必须在身份验证函数的custom callback(向下滚动页面)内使用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 文档可以做一些大修,因为它们缺乏细节和link to non-existent examples。
This answer 帮助我了解缺少凭据消息是在 info
参数中传递的。
【讨论】:
以上是关于带有 Passport 的 Node.js 身份验证:如果缺少字段,如何闪烁消息?的主要内容,如果未能解决你的问题,请参考以下文章
Node.js Passport 的 Google 策略上的自定义 returnUrl
有人使用带有 OAuth2.0 身份验证系统的 node.js 吗? [关闭]
Node.js - JWT、API:如何实现多个 Passport.js 身份验证中间件?
node.js - Passport 不会在浏览器请求中持续存在,可与 Postman 一起使用
使用 passport-auth0 进行 MEAN Stack 用户身份验证,在 Angular 中调用 Node Js passport-auth0 API