Mengalihkan ke halaman sebelumnya setelah otentikasi di node.js menggunakan passport.js

101

Saya mencoba membuat mekanisme login menggunakan node.js, express, dan passport.js. Login itu sendiri berfungsi cukup bagus, juga sesi disimpan dengan baik dengan redis tetapi saya memiliki beberapa masalah dengan mengarahkan pengguna ke tempat dia memulai sebelum diminta untuk mengotentikasi.

mis. Tautan mengikuti pengguna http://localhost:3000/hiddenkemudian dialihkan ke http://localhost:3000/logintetapi kemudian saya ingin dia dialihkan lagi ke http://localhost:3000/hidden.

Tujuannya adalah, jika pengguna mengakses secara acak halaman yang dia butuhkan untuk masuk terlebih dahulu, dia akan dialihkan ke situs / login dengan memberikan kredensial dan kemudian dialihkan kembali ke situs yang sebelumnya dia coba akses.

Ini posting login saya

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

dan di sini Metode sureAuthenticated saya

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

yang terhubung ke /hiddenhalaman

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

Output html untuk situs login cukup sederhana

<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>
Alx
sumber
Hai, saya juga melakukan hal yang sama, satu-satunya perbedaan adalah ketika pengguna berhasil masuk maka saya akan mengarahkan ke halaman welcome.html dengan pesan seperti 'Nama Pengguna Selamat Datang'. dengar UserName akan menjadi LoginName-nya. Dapatkah Anda menunjukkan kepada saya cara meneruskan nilai kotak teks ke halaman berikutnya ??
Dhrumil Shah
tidak tahu banyak, saya kira hanya akan melewatinya. Menurut contoh saya yang mungkin: res.render ('hidden', {title: 'hidden page', username: 'Tyrion Lannister'});
Alx

Jawaban:

84

Saya tidak tahu tentang paspor, tapi begini cara saya melakukannya:

Saya memiliki middleware yang saya gunakan dengan app.get('/account', auth.restrict, routes.account)set itu redirectTodi sesi ... lalu saya mengarahkan ke / login

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

Kemudian di routes.login.postsaya lakukan hal berikut:

var redirectTo = req.session.redirectTo || '/';
delete req.session.redirectTo;
// is authenticated ?
res.redirect(redirectTo);
chovy
sumber
terima kasih untuk balasan Anda. Bukankah Anda menyetel redirect / akun langsung di kode Anda? Apa yang terjadi jika Anda memiliki tautan lain, katakanlah / pro_accounts menggunakan auth.restrict yang sama, mereka akan dialihkan ke / akun ....
Alx
4
Itu benar. Saya selalu mengalihkan ke / akun setelah mereka masuk, tetapi Anda dapat menggantinya denganreq.session.redirect_to = req.path
chovy
1
hm .. kurang tepat yang saya cari. Saya memanggil sureAuthenticated untuk mencari tahu apakah pengguna sudah diautentikasi atau belum, jika belum dialihkan ke / login. Dari tampilan ini saya perlu cara untuk kembali ke / akun. Memanggil req.path di dalam / login memberi saya sederhana / login kembali. Menggunakan referer juga tidak berfungsi dan itu tidak begitu pasti apakah browser mengatur referer dengan benar ...
Alx
6
Anda perlu menyetel req.session.redirect_to = req.pathdi dalam middleware auth Anda. Kemudian baca dan hapus di login.postrute.
chovy
ini bukan solusi yang bagus untuk paspor yang menerima successRedirect sebagai opsi pada saat permintaan tidak tersedia
linuxdan
110

Dalam ensureAuthenticatedmetode Anda, simpan url kembali dalam sesi seperti ini:

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

Kemudian Anda dapat memperbarui passport.authenticate rute Anda ke sesuatu seperti:

app.get('/auth/google/return', passport.authenticate('google'), function(req, res) {
    res.redirect(req.session.returnTo || '/');
    delete req.session.returnTo;
}); 
linuxdan.dll
sumber
7
Bekerja pada izin pertama, tetapi saya perlu menambahkan yang berikut ini setelah pengalihan di rute passport.authenticate agar tidak dibawa kembali ke alamat returnTo setelah logout / login berikutnya: req.session.returnTo = null;Lihat pertanyaan ini untuk info tambahan tentang logout default paspor, yang, pada pemeriksaan sumber, tampaknya hanya menghapus session.user, dan bukan seluruh sesi.
Rob Andren
bisakah saya meneruskan kueri seperti? callback = / profile alih-alih sesi
OMGPOP
@OMGPOP Anda mungkin bisa, tetapi kedengarannya tidak begitu aman, adakah alasan Anda tidak ingin memanfaatkan sesi ini?
linuxdan
@linuxdan tidak sama sekali. tetapi hanya merepotkan untuk mengekstrak tautan pengalihan keluar dan dimasukkan ke dalam sesi, lalu ketika pengguna mengalihkan kembali, tetapkan kembali tautan pengalihan dari sesi
OMGPOP
5
Alih-alih req.pathsaya akan menggunakan req.originalUrlkarena ini adalah kombinasi baseUrl dan path, lihat dokumentasinya
matthaeus
14

Lihatlah connect-sure-login , yang bekerja di samping Passport untuk melakukan apa yang Anda inginkan!

Jared Hanson
sumber
7

Cara saya melakukan sesuatu:

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

GET / pengontrol login :

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

res.render('account/login')

Pengontrol POST / login :

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

  res.redirect(returnTo);

Pengontrol POST / logout (tidak yakin apakah ada 100% ok, komentar dipersilakan):

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

Hapus returnTo middleware (hapus returnTo dari sesi di rute mana pun kecuali rute auth - bagi saya mereka adalah / login dan / 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()
})

Pendekatan ini memiliki dua fitur :

  • Anda dapat melindungi beberapa rute dengan middleware isAuthenticated ;
  • di halaman mana pun Anda cukup mengklik URL login, dan setelah login kembali ke halaman itu;
deksden
sumber
Ada begitu banyak tanggapan yang baik di sini, saya dapat menggunakan contoh Anda dan membuatnya berhasil dengan proyek saya. Terima kasih!
pengguna752746
5

Jika Anda menggunakan connect-sure-login, ada cara super-mudah dan terintegrasi untuk melakukan ini dengan Passport menggunakan successReturnToOrRedirectparameter. Saat digunakan, paspor akan mengirim Anda kembali ke URL yang diminta semula atau kembali ke URL yang Anda berikan.

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

igneosaur
sumber
Ini sepertinya duplikat dari jawaban @ jaredhanson
mikemaccana
1

Jawaban @chovy dan @linuxdan memiliki bug dengan tidak membersihkan session.returnTojika pengguna pergi ke halaman lain setelah pengalihan login (itu tidak memerlukan otentikasi) dan masuk melalui sana. Jadi tambahkan kode ini ke implementasinya:

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

Jika Anda melakukan beberapa permintaan ajax dari halaman login, Anda juga dapat mengecualikannya.


Pendekatan lainnya adalah dengan menggunakan flash inensureAuthenticated

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

Dan kemudian di GET login

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

Dalam tampilan tambahkan bidang tersembunyi ke formulir login (contoh di giok)

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

Dalam login POST

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

Perhatikan bahwa redirectTo akan dihapus setelah GET login terlebih dahulu.

Alexander Danilov
sumber
0

Cara termudah (dan benar) untuk mencapai ini adalah pengaturan failureRedirectdan successRedirectopsi .

Eray
sumber
Bisakah Anda menjelaskannya? Saya bisa melihat successRedirectsedang digunakan pada rute pulang, tapi kemudian tujuan yang dimaksud (yaitu di returnToatas) akan hilang, bukan?
Tom Söderlund