Chuyển hướng đến trang trước sau khi xác thực trong node.js bằng passport.js


99

Tôi đang cố gắng thiết lập cơ chế đăng nhập bằng node.js, express và passport.js. Bản thân Đăng nhập hoạt động khá tốt, các phiên cũng được lưu trữ độc đáo với redis nhưng tôi gặp một số rắc rối với việc chuyển hướng người dùng đến nơi anh ta bắt đầu trước khi được nhắc xác thực.

Ví dụ: người dùng theo liên kết http://localhost:3000/hiddensau đó được chuyển hướng đến http://localhost:3000/loginnhưng sau đó tôi muốn anh ta được chuyển hướng trở lại http://localhost:3000/hidden.

Mục đích của việc này là, nếu người dùng truy cập ngẫu nhiên một trang mà anh ta cần đăng nhập trước, anh ta sẽ được chuyển hướng đến trang / đăng nhập cung cấp thông tin đăng nhập của mình và sau đó được chuyển hướng trở lại trang mà anh ta đã cố gắng truy cập trước đó.

Đây là bài đăng nhập của tôi

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

và đây là phương pháp ensureAuthenticated của tôi

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

cái nào kết nối vào /hiddentrang

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

Đầu ra html cho trang đăng nhập khá đơn giản

<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>

Xin chào, tôi cũng đang làm tương tự, chỉ khác là khi người dùng đăng nhập thành công thì tôi sẽ chuyển hướng đến trang welcome.html với thông báo như 'Welcome UserName'. nghe Tên người dùng sẽ là Tên đăng nhập của anh ấy / cô ấy. Bạn có thể chỉ cho tôi cách chuyển giá trị hộp văn bản sang trang tiếp theo không?
Dhrumil Shah,

không biết nhiều, tôi đoán đơn giản là vượt qua nó. Theo ví dụ của tôi, đó có thể là: res.render ('hidden', {title: 'hidden page', tên người dùng: 'Tyrion Lannister'});
Alx

Câu trả lời:


84

Tôi không biết về hộ chiếu, nhưng đây là cách tôi thực hiện:

Tôi có một phần mềm trung gian mà tôi sử dụng với app.get('/account', auth.restrict, routes.account)bộ đó redirectTotrong phiên ... sau đó tôi chuyển hướng đến / đăng nhập

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

Sau đó, routes.login.posttôi làm như sau:

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

Cảm ơn vì đã trả lời. Bạn không đặt chuyển hướng / tài khoản trực tiếp trong mã của mình? Chuyện gì xảy ra nếu bạn có tiếng nói khác liên kết cho phép của / pro_accounts sử dụng auth.restrict cùng họ sẽ được chuyển hướng đến / tài khoản ....
Alx

4
Đúng. Tôi luôn luôn chuyển hướng đến / tài khoản sau khi đăng nhập, nhưng bạn có thể thay thế nó bằngreq.session.redirect_to = req.path
chovy

1
hm .. nó không hoàn toàn là tôi đang tìm kiếm. Tôi gọi là ensureAuthenticated để tìm hiểu xem người dùng đã được xác thực hay chưa, nếu chưa, nó sẽ được chuyển hướng đến / đăng nhập. Từ chế độ xem này, tôi cần một cách để quay lại tài khoản /. Gọi req.path trong / đăng nhập giúp tôi đăng nhập lại / đơn giản. Sử dụng referer không làm việc hoặc cộng của nó không hoàn toàn chắc chắn nếu trình duyệt đặt referer đúng cách ...
Alx

6
Bạn cần đặt req.session.redirect_to = req.pathbên trong phần mềm trung gian auth của mình. Sau đó đọc nó và xóa nó trong login.postlộ trình.
chovy

đây không phải là một giải pháp tuyệt vời cho hộ chiếu mà chấp nhận successRedirect như một tùy chọn tại một điểm rằng yêu cầu không có sẵn
linuxdan

110

Trong ensureAuthenticatedphương thức của bạn, hãy lưu url trả về trong phiên như thế này:

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

Sau đó, bạn có thể cập nhật lộ trình passport.authenticate của mình thành một cái gì đó như:

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

7
Đã làm việc với lần vượt qua đầu tiên, nhưng tôi cần thêm thông tin sau sau chuyển hướng trong tuyến passport.authenticate để tránh bị đưa trở lại địa chỉ returnTo sau lần đăng xuất / đăng nhập tiếp theo: req.session.returnTo = null;Xem câu hỏi này để biết thêm thông tin về đăng xuất mặc định của hộ chiếu, khi kiểm tra nguồn, dường như chỉ xóa session.user, chứ không phải toàn bộ phiên.
Rob Andren

tôi có thể chỉ đơn giản là vượt qua xung quanh một truy vấn như callback = / hồ sơ thay vì phiên?
omgpop

@OMGPOP bạn có thể có, nhưng điều đó có vẻ không an toàn lắm, có lý do gì khiến bạn không muốn tận dụng phiên này?
linuxdan

@linuxdan không hề. nhưng nó chỉ là phiền hà để trích xuất các liên kết chuyển hướng ra ngoài và đưa vào phiên giao dịch, sau đó khi người dùng chuyển hướng trở lại, trở lại assign vào liên kết chuyển hướng từ phiên
omgpop

5
Thay vì req.pathtôi sẽ sử dụng req.originalUrlvì nó là một sự kết hợp của baseUrl và đường dẫn, xem tài liệu
Matthaeus


7

Cách làm của tôi:

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

GET / bộ điều khiển đăng nhập :

if( req.query.origin )
  req.session.returnTo = req.query.origin
else
  req.session.returnTo = req.header('Referer')

res.render('account/login')

ĐĂNG / bộ điều khiển đăng nhập :

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

  res.redirect(returnTo);

Bộ điều khiển POST / đăng xuất (không chắc chắn nếu có 100% ok, nhận xét được hoan nghênh):

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

Xóa phần mềm trung gian returnTo (xóa returnTo khỏi phiên trên bất kỳ tuyến đường nào ngoại trừ các tuyến đường xác thực - đối với tôi chúng là / đăng nhập và / auth /: nhà cung cấp):

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

Cách tiếp cận này có hai đặc điểm :

  • bạn có thể bảo vệ một số tuyến đường bằng phần mềm trung gian isAuthenticated ;
  • trên bất kỳ trang nào, bạn có thể chỉ cần nhấp vào URL đăng nhập, và sau khi đăng nhập trở lại trang đó;

Có rất nhiều phản hồi tốt ở đây, tôi có thể sử dụng ví dụ của bạn và làm cho nó hoạt động với dự án của tôi. Cảm ơn bạn!
user752746 10/02

5

Nếu bạn đang sử dụng kết nối-đảm bảo-đăng nhập, có một cách tích hợp siêu dễ dàng để thực hiện việc này với Passport bằng cách sử dụng successReturnToOrRedirecttham số. Khi được sử dụng, hộ chiếu sẽ đưa bạn trở lại URL được yêu cầu ban đầu hoặc dự phòng cho URL bạn cung cấp.

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


Điều này có vẻ giống như một bản sao của một câu trả lời @ jaredhanson của
mikemaccana

1

Câu trả lời @chovy và @linuxdan có lỗi không xóa session.returnTonếu người dùng truy cập trang khác sau khi chuyển hướng đăng nhập (điều đó không yêu cầu xác thực) và đăng nhập qua đó. Vì vậy, hãy thêm mã này vào các triển khai của chúng:

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

Nếu bạn thực hiện một số yêu cầu ajax từ trang đăng nhập, bạn cũng có thể loại trừ chúng.


Một cách tiếp cận khác là sử dụng flash trongensureAuthenticated

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

Và sau đó đăng nhập GET

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

Trong chế độ xem, hãy thêm trường ẩn vào biểu mẫu đăng nhập (ví dụ bằng ngọc bích)

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

Trong đăng nhập ĐĂNG

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

Lưu ý rằng redirectTo sẽ xóa sau lần đầu tiên GET đăng nhập với nó.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.