使用password.js在node.js中进行身份验证后重定向到上一页


100

我正在尝试使用node.js,express和passport.js建立登录机制。Login本身工作得很好,会话也可以很好地存储在redis上,但是在提示用户进行身份验证之前,将用户重定向到他从那里开始时确实存在一些麻烦。

例如,用户跟随链接http://localhost:3000/hidden,然后重定向到,http://localhost:3000/login但是我希望他再次重定向回http://localhost:3000/hidden

这样做的目的是,如果用户随机访问需要首先登录的页面,则应将其重定向到提供其凭据的/ 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);
});

这里是我的sureAuthenticated方法

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' });
});

登录站点的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>

嗨,我也做了同样的事情,唯一的区别是,当用户成功登录后,我将重定向到带有“ Welcome UserName”之类的消息的welcome.html页面。听到UserName将是他/她的LoginName。您能告诉我如何将文本框值传递给下一页吗?
Dhrumil Shah

不知道太多,我想只是通过它。根据我的示例,可能是:res.render('hidden',{title:'hidden page',username:'Tyrion Lannister'});
Alx

Answers:


84

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

我有一个中间件我使用app.get('/account', auth.restrict, routes.account)的是套redirectTo在会议...然后我重定向到/登录

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 || '/';
delete req.session.redirectTo;
// is authenticated ?
res.redirect(redirectTo);

感谢您的回复。您不是在代码中直接设置重定向/帐户吗?如果您有另一个链接,比如说使用相同的auth.restrict的/ pro_accounts,将被重定向到/ account ...,会发生什么情况。–
Alx

4
确实如此。他们登录后,我总是重定向到/ account,但是您可以将其替换为req.session.redirect_to = req.path
chovy

1
嗯..不是我要找的。我调用sureAuthenticated来确定用户是否已经通过身份验证,如果不是,则将其重定向到/ login。从这个角度来看,我需要一种返回/ account的方法。在/ login内调用req.path给我简单的/ login。使用引荐来源网址既不起作用,也不确定浏览器是否正确设置了引荐来源...
Alx 2012年

6
您需要req.session.redirect_to = req.path在身份验证中间件中进行设置。然后阅读并删除它在login.post路线。
chovy

这对于护照来说不是一个很好的解决方案,因为它在没有请求的情况下接受了successRedirect作为选项
linuxdan 2014年

110

在您的ensureAuthenticated方法中,将返回网址保存在会话中,如下所示:

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

然后,您可以将您的password.authenticate路由更新为以下内容:

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

7
进行了第一次通过,但是我需要在passport.authenticate路由中的重定向之后添加以下内容,以避免在后续注销/登录后被带回returnTo地址:有关护照默认注销的更多信息,req.session.returnTo = null;请参阅此问题,在检查源代码时,似乎只能清除session.user,而不是整个会话。
Rob Andren 2015年

我可以简单地绕过诸如?callback = / profile之类的查询而不是会话吗
OMGPOP

您可能可以使用@OMGPOP,但这听起来不太安全,是否有您不想利用此会话的原因?
linuxdan

@linuxdan一点也不。但是将重定向链接提取出来并放入会话中很麻烦,然后当用户重定向回去时,从会话中分配重定向链接
OMGPOP,2015年

5
相反的req.path,我会用req.originalUrl,因为它的baseUrl和路径的组合,看文档
马特乌斯


7

我的做事方式:

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 /登录控制器:

  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中间件保护某些路由;
  • 您可以在任何页面上单击登录URL,然后在登录后返回该页面;

这里有很多很好的响应,我能够使用您的示例并将其用于我的项目。谢谢!
user752746 '18

5

如果您使用的是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


这似乎是@jaredhanson答案的重复
mikemaccana

1

@chovy和@linuxdan答案存在无法清除登录重定向(无需身份验证)并通过该页面登录后用户是否进入另一页面的错误session.returnTo。因此,将以下代码添加到其实现中:

// 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()
})

如果您在登录页面上执行了一些ajax请求,则也可以排除它们。


另一种方法是使用闪存ensureAuthenticated

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

然后在GET登录

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

在视图中,将隐藏字段添加到登录表单(例如玉石)

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

在POST登录中

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

请注意,首次使用GET登录后,redirectTo将清除。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.