使用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后清除。
设置failureRedirect
和successRedirect
选项是最简单的(也是正确的)。