【问题标题】:Redirecting to previous page after authentication in node.js using passport.js使用passport.js在node.js中进行身份验证后重定向到上一页
【发布时间】:2012-10-31 09:56:41
【问题描述】:

我正在尝试使用 node.js、express 和 passport.js 建立登录机制。登录本身工作得很好,会话也可以很好地使用 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);
});

这里是我的 ensureAuthenticated 方法

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.html页面,并显示类似“欢迎用户名”的消息。听到用户名将是他/她的登录名。你能告诉我如何将文本框值传递到下一页吗??
  • 不太了解,我猜只是通过它。根据我的示例,可能是: res.render('hidden', {title: 'hidden page', username: 'Tyrion Lannister'});

标签: node.js authentication redirect express passport.js


【解决方案1】:

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

...
req.session.returnTo = req.originalUrl; 
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;
}); 

【讨论】:

  • 第一遍工作,但是我需要在passport.authenticate路由中重定向之后添加以下内容,以避免在后续注销/登录后被带回returnTo地址:req.session.returnTo = null;见@ 987654321@ 获取有关护照默认注销的更多信息,在检查源时,它似乎只清除 session.user,而不是整个会话。
  • 我可以简单地传递一个查询吗?callback=/profile 而不是会话
  • @OMGPOP 您可能可以,但这听起来不太安全,您是否有不想利用会话的原因?
  • @linuxdan 一点也不。但是将重定向链接提取出来并放入会话中很麻烦,然后当用户重定向回来时,从会话中分配回重定向链接
  • 我会使用req.originalUrl 而不是req.path,因为它是baseUrl 和路径的组合,请参阅the documentation
【解决方案2】:

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

我有一个与app.get('/account', auth.restrict, routes.account) 一起使用的中间件,它在会话中设置redirectTo...然后我重定向到/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 || '/';
delete req.session.redirectTo;
// is authenticated ?
res.redirect(redirectTo);

【讨论】:

  • 感谢您的回复。您不是在代码中直接设置重定向/帐户吗?如果您有另一个链接,假设 /pro_accounts 使用相同的 auth.restrict 会发生什么情况,它们将被重定向到 /account ....
  • 确实如此。我总是在他们登录后重定向到 /account,但您可以将其替换为 req.session.redirect_to = req.path
  • hm.. 它不是我要找的。我调用 ensureAuthenticated 来确定用户是否已经通过身份验证,如果没有,则将其重定向到 /login。从这个角度来看,我需要一种方法来返回 /account。在 /login 中调用 req.path 可以让我返回简单的 /login。使用 referer 也不起作用,而且它不确定浏览器是否正确设置了 referer...
  • 您需要在您的身份验证中间件中设置req.session.redirect_to = req.path。然后在login.post路由中阅读并删除。
  • 对于在请求不可用时接受 successRedirect 作为选项的护照来说,这不是一个很好的解决方案
【解决方案3】:

实现这一目标的最简单(和正确)方法是setting failureRedirect and successRedirect options

【讨论】:

  • 你能详细说明一下吗?我可以看到successRedirect 在返回路线上使用,但是预期的目的地(即上面的returnTo)会丢失,对吗?
【解决方案4】:

我的做事方式:

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 /logout 控制器(不确定是否 100% ok,欢迎使用 cmets):

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

清除 returnTo 中间件(从会话中清除 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,登录后返回到该页面;

【讨论】:

  • 这里有很多好的反应,我能够使用您的示例并使其适用于我的项目。谢谢!
【解决方案5】:

如果您使用的是connect-ensure-login,则可以通过使用successReturnToOrRedirect 参数的Passport 以一种超级简单的集成方式执行此操作。使用时,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 的回答重复
  • 值得补充的是,express-session 需要进行配置,以便保存“未初始化”的 cookie:{ saveUninitialized: true }。这意味着匿名用户将为他们创建会话,并对您的会话存储施加更大的压力。从好的方面来说,这些会话将存储 returnTo 位置,并在匿名用户成功验证后使用。
【解决方案6】:

@chovy 和 @linuxdan 的答案有 bug 如果用户在登录重定向后转到另一个页面(不需要身份验证)并通过那里登录,则不会清除 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中使用flash

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

然后在 GET 中登录

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

在视图中添加隐藏字段到登录表单(jade 中的示例)

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

在 POST 登录中

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

请注意,redirectTo 将在第一次 GET 登录后清除。

【讨论】:

    【解决方案7】:

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

    【讨论】:

      猜你喜欢
      • 2023-04-05
      • 2018-03-26
      • 2023-04-03
      • 1970-01-01
      • 2019-06-30
      • 2018-03-08
      • 2018-05-03
      • 1970-01-01
      • 2017-08-19
      相关资源
      最近更新 更多