Reindirizzamento alla pagina precedente dopo l'autenticazione in node.js utilizzando passport.js


101

Sto cercando di stabilire un meccanismo di accesso utilizzando node.js, express e passport.js. Il login stesso funziona abbastanza bene, anche le sessioni sono memorizzate bene con redis ma ho alcuni problemi con il reindirizzamento dell'utente al punto da cui è partito prima che mi venga chiesto di autenticarsi.

Ad esempio, l'utente segue il collegamento http://localhost:3000/hiddenviene quindi reindirizzato a, http://localhost:3000/loginma poi voglio che venga reindirizzato nuovamente a http://localhost:3000/hidden.

Lo scopo di ciò è, se l'utente accede in modo casuale a una pagina a cui deve prima essere loggato, verrà reindirizzato al sito / login fornendo le sue credenziali e quindi reindirizzato nuovamente al sito a cui ha precedentemente tentato di accedere.

Ecco il mio post di accesso

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

e qui il mio metodo sureAuthenticated

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

che si aggancia alla /hiddenpagina

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

L'output html per il sito di accesso è abbastanza semplice

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

Ciao, sto anche facendo lo stesso, l'unica differenza è quando l'utente accede con successo, quindi reindirizzerò alla pagina welcome.html con un messaggio come "Welcome UserName". sentire UserName sarà il suo LoginName. Potete mostrarmi come passare il valore della casella di testo alla pagina successiva ??
Dhrumil Shah

non sapendo molto, immagino semplicemente di passarlo attraverso. Secondo il mio esempio potrebbe essere: res.render ('hidden', {title: 'hidden page', username: 'Tyrion Lannister'});
Alx

Risposte:


84

Non so del passaporto, ma ecco come lo faccio:

Ho un middleware che uso con app.get('/account', auth.restrict, routes.account)quei set redirectTonella sessione ... quindi reindirizzo a / login

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

Quindi in routes.login.postfaccio quanto segue:

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

grazie per la tua risposta. Non stai impostando il reindirizzamento / account direttamente nel tuo codice? Cosa succede se hai un altro link diciamo / pro_accounts che usa lo stesso auth.restrict verrebbero reindirizzati a / account ....
Alx

4
È vero. Reindirizzo sempre a / account dopo il login, ma potresti sostituirlo conreq.session.redirect_to = req.path
chovy

1
hm .. non è proprio quello che sto cercando. ChiamouranceAuthenticated per capire se un utente è già autenticato o meno, in caso contrario viene reindirizzato a / login. Da questo punto di vista ho bisogno di un modo per tornare a / account. Chiamare req.path all'interno di / login mi restituisce semplice / login. L'uso del referer non funziona e non è del tutto certo se il browser imposta correttamente il referer ...
Alx

6
È necessario impostare req.session.redirect_to = req.pathall'interno del middleware di autenticazione. Quindi leggilo e cancellalo nel login.postpercorso.
chovy

questa non è un'ottima soluzione per il passaporto che accetta il successo Reindirizza come opzione in un punto in cui la richiesta non è disponibile
linuxdan

110

Nel tuo ensureAuthenticatedmetodo salva l'URL di ritorno nella sessione in questo modo:

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

Quindi puoi aggiornare il tuo percorso passport.authenticate a qualcosa di simile:

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

7
Ho lavorato al primo passaggio, ma avevo bisogno di aggiungere quanto segue dopo il reindirizzamento nel percorso passport.authenticate per evitare di essere riportato all'indirizzo returnTo dopo il successivo logout / login: req.session.returnTo = null;vedere questa domanda per ulteriori informazioni sul logout predefinito del passport, che, all'esame della fonte, sembra che cancelli solo il file session.user e non l'intera sessione.
Rob Andren

posso semplicemente passare una query come? callback = / profile invece di session
OMGPOP

@OMGPOP probabilmente potresti, ma non sembra molto sicuro, c'è una ragione per cui non vuoi approfittare della sessione?
linuxdan

@linuxdan per niente. ma è solo fastidioso estrarre il link di reindirizzamento e metterlo in sessione, quindi quando l'utente reindirizza di nuovo, riassegna il link di reindirizzamento dalla sessione
OMGPOP

5
Invece di req.pathutilizzarlo req.originalUrlpoiché è una combinazione di baseUrl e path, vedere la documentazione
matthaeus


7

Il mio modo di fare le cose:

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

GET / controller di accesso :

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

res.render('account/login')

Controller POST / login :

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

  res.redirect(returnTo);

Controller POST / logout (non sono sicuro che sia ok al 100%, i commenti sono benvenuti):

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

Clear returnTo middleware (cancella returnTo dalla sessione su qualsiasi route tranne le route di autenticazione - per me sono / login e / 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()
})

Questo approccio ha due caratteristiche :

  • puoi proteggere alcune rotte con il middleware isAuthenticated ;
  • in qualsiasi pagina puoi semplicemente fare clic sull'URL di accesso e, dopo il login, tornare a quella pagina;

Ci sono state così tante buone risposte qui, ho potuto usare il tuo esempio e farlo funzionare con il mio progetto. Grazie!
user752746

5

Se stai usando la connessione-sicurezza-login, c'è un modo super facile e integrato per farlo con Passport usando il successReturnToOrRedirectparametro. Se utilizzato, passport ti rimanderà all'URL richiesto originariamente o riporterà l'URL fornito.

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


Questo sembra un duplicato della risposta di @ jaredhanson
mikemaccana

1

Le risposte di @chovy e @linuxdan hanno un bug con la mancata cancellazione session.returnTose l'utente va a un'altra pagina dopo il reindirizzamento del login (che non richiede l'autenticazione) e accede da lì. Quindi aggiungi questo codice alle loro implementazioni:

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

Se esegui alcune richieste ajax dalla pagina di accesso, puoi anche escluderle.


Un altro approccio consiste nell'utilizzare il flash inensureAuthenticated

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

E poi in OTTIENI il login

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

Nella visualizzazione aggiungi un campo nascosto al modulo di accesso (esempio in jade)

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

In POST login

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

Si noti che redirectTo verrà cancellato dopo il primo accesso GET con esso.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.