使用passport.js在node.js中进行身份validation后,redirect到上一页

我试图build立一个使用node.js,express和passport.js的login机制。 login本身工作得相当不错,会话也很好地存储在redis中,但是在提示用户进行身份validation之前,将用户redirect到他所在的位置时遇到了一些麻烦。

例如,用户链接http://localhost:3000/hidden然后被redirect到http://localhost:3000/login但是我希望他再次被redirect到http://localhost:3000/hidden

这样做的目的是,如果用户随机访问一个页面,他需要首先login,他应该被redirect到/ login站点提供他的凭据,然后被redirect到他以前试图访问的网站。

这是我的login信息

 app.post('/login', function (req, res, next) { passport.authenticate('local', function (err, user, info) { if (err) { return next(err) } else if (!user) { console.log('message: ' + info.message); return res.redirect('/login') } else { req.logIn(user, function (err) { if (err) { return next(err); } return next(); // <-? Is this line right? }); } })(req, res, next); }); 

在这里我保证authentication的方法

 function ensureAuthenticated (req, res, next) { if (req.isAuthenticated()) { return next(); } res.redirect('/login'); } 

挂钩到/hidden页面

 app.get('/hidden', ensureAuthenticated, function(req, res){ res.render('hidden', { title: 'hidden page' }); }); 

login网站的html输出非常简单

 <form method="post" action="/login"> <div id="username"> <label>Username:</label> <input type="text" value="bob" name="username"> </div> <div id="password"> <label>Password:</label> <input type="password" value="secret" name="password"> </div> <div id="info"></div> <div id="submit"> <input type="submit" value="submit"> </div> </form> 

我不知道护照,但我是这样做的:

我有一个中间件,我使用app.get('/account', auth.restrict, routes.account)在会话中设置redirectTo …然后我redirect到/login

 auth.restrict = function(req, res, next){ if (!req.session.userid) { req.session.redirectTo = '/account'; res.redirect('/login'); } else { next(); } }; 

然后在routes.login.post我做到以下几点:

 var redirectTo = req.session.redirectTo ? req.session.redirectTo : '/'; delete req.session.redirectTo; // is authenticated ? res.redirect(redirectTo); 

在你的ensureAuthenticated方法中保存会话中的返回url,如下所示:

 ... req.session.returnTo = req.path; res.redirect('/login'); ... 

然后你可以更新你的passport.authenticate路由到像这样的东西:

 app.get('/auth/google/return', passport.authenticate('google'), function(req, res) { res.redirect(req.session.returnTo || '/'); delete req.session.returnTo; }); 

看一下connect-ensure-login ,它和Passport一起工作,完全符合你的要求!

如果您正在使用connect-ensure-login,那么使用successReturnToOrRedirect参数可以使用Passport来执行此操作。 使用护照时,护照会将您发送回原始请求的URL或回退到您提供的URL。

 router.post('/login', passport.authenticate('local', { successReturnToOrRedirect: '/user/me', failureRedirect: '/user/login', failureFlash: true })); 

https://github.com/jaredhanson/connect-ensure-login#log-in-and-return-to

我做事的方式:

 const isAuthenticated = (req, res, next) => { if (req.isAuthenticated()) { return next() } res.redirect( `/login?origin=${req.originalUrl}` ) }; 

GET /login控制器:

 if( req.query.origin ) req.session.returnTo = req.query.origin else req.session.returnTo = req.header('Referer') res.render('account/login') 

POST /login控制器:

  let returnTo = '/' if (req.session.returnTo) { returnTo = req.session.returnTo delete req.session.returnTo } res.redirect(returnTo); 

POST /注销控制器(不知道是否有100%的好评,欢迎评论):

 req.logout(); res.redirect(req.header('Referer') || '/'); if (req.session.returnTo) { delete req.session.returnTo } 

清除returnTo中间件 (除auth路由之外,清除returnTo从任何路由的会话 – 对我来说,他们是/ login和/ auth /:provider):

 String.prototype.startsWith = function(needle) { return(this.indexOf(needle) == 0) } app.use(function(req, res, next) { if ( !(req.path == '/login' || req.path.startsWith('/auth/')) && req.session.returnTo) { delete req.session.returnTo } next() }) 

这种方法有两个特点

  • 你可以用isAuthenticated中间件保护一些路由;
  • 在任何一页上你都可以简单的点击loginurl,login后回到那个页面;

@chovy和@linuxdan的答案有没有清除session.returnTo如果用户转到另一个页面后loginredirect(即不需要身份validation),并通过那里login的错误。 所以把这个代码添加到他们的实现中

 // clear session.returnTo if user goes to another page after redirect to login app.use(function(req, res, next) { if (req.path != '/login' && req.session.returnTo) { delete req.session.returnTo } next() }) 

如果你从login页面做一些Ajax请求,你也可以排除它们。


另一种方法是在ensureAuthenticated使用flash

 req.flash('redirectTo', req.path) res.redirect('/login') 

然后在GETlogin

 res.render('login', { redirectTo: req.flash('redirectTo') }) 

在视图中添加隐藏字段login窗体(以玉为例)

 if (redirectTo != '') input(type="hidden" name="redirectTo" value="#{redirectTo}") 

在POSTlogin

 res.redirect(req.body.redirectTo || '/') 

请注意,redirectTo将在首次GETlogin后清除。

设置failureRedirectsuccessRedirect选项是最容易的(也是正确的)。