เปลี่ยนเส้นทางไปยังหน้าที่แล้วหลังจากการพิสูจน์ตัวตนใน node.js โดยใช้ passport.js


103

ฉันกำลังพยายามสร้างกลไกการเข้าสู่ระบบโดยใช้ node.js, express และ passport.js การเข้าสู่ระบบใช้งานได้ค่อนข้างดีนอกจากนี้เซสชันจะถูกจัดเก็บไว้อย่างดีด้วยการ redis แต่ฉันมีปัญหาในการเปลี่ยนเส้นทางผู้ใช้ไปยังจุดที่เขาเริ่มต้นก่อนที่จะได้รับแจ้งให้ตรวจสอบสิทธิ์

เช่นผู้ใช้ดังต่อไปนี้การเชื่อมโยงhttp://localhost:3000/hiddenถูกเปลี่ยนเส้นทางไปแล้วแต่แล้วฉันต้องการให้เขาถูกนำกลับมาอีกครั้งเพื่อhttp://localhost:3000/loginhttp://localhost:3000/hidden

จุดประสงค์ของสิ่งนี้คือหากผู้ใช้เข้าถึงเพจโดยสุ่มที่เขาต้องเข้าสู่ระบบก่อนเขาจะถูกเปลี่ยนเส้นทางไปยังไซต์ / ล็อกอินโดยให้ข้อมูลประจำตัวของเขาจากนั้นจะถูกเปลี่ยนเส้นทางกลับไปยังไซต์ที่เขาพยายามเข้าถึงก่อนหน้านี้

นี่คือโพสต์ล็อกอินของฉัน

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.html พร้อมข้อความเช่น 'ยินดีต้อนรับชื่อผู้ใช้' ได้ยิน UserName จะเป็น LoginName ของเขา / เธอ ช่วยแสดงวิธีส่งค่ากล่องข้อความไปหน้าถัดไปได้ไหม?
Dhrumil Shah

ไม่รู้อะไรมากฉันเดาเพียงว่าผ่านมันไป ตามตัวอย่างของฉันที่อาจจะ: res.render ('hidden', {title: 'hidden page', username: 'Tyrion Lannister'});
Alx

คำตอบ:


85

ฉันไม่รู้เกี่ยวกับหนังสือเดินทาง แต่ฉันต้องทำอย่างไร:

ฉันมีมิดเดิลแวร์ที่ฉันใช้กับ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);

ขอบคุณสำหรับการตอบกลับของคุณ. คุณไม่ได้ตั้งค่าการเปลี่ยนเส้นทาง / บัญชีโดยตรงในรหัสของคุณหรือ จะเกิดอะไรขึ้นถ้าคุณมีลิงค์อื่นสมมติว่า / pro_accounts ใช้ auth.restrict เดียวกันซึ่งจะถูกเปลี่ยนเส้นทางไปยัง / account ....
Alx

4
นั่นคือเรื่องจริง ฉันเปลี่ยนเส้นทางไปยัง / บัญชีทุกครั้งหลังจากที่พวกเขาเข้าสู่ระบบ แต่คุณสามารถแทนที่ด้วยreq.session.redirect_to = req.path
chovy

1
หืม .. มันไม่ค่อยฉันกำลังมองหา ฉันเรียกว่า sureAuthenticated เพื่อดูว่าผู้ใช้ได้รับการตรวจสอบแล้วหรือไม่หากไม่ถูกเปลี่ยนเส้นทางไปยัง / login จากมุมมองนี้ฉันต้องการวิธีกลับไปที่ / บัญชี การเรียกใช้ req.path ภายใน / login ทำให้ฉันง่าย / เข้าสู่ระบบกลับ การใช้ referer ก็ไม่ได้ผลบวกกับมันไม่ค่อยแน่ใจว่าเบราว์เซอร์ตั้งค่า referer อย่างถูกต้องหรือไม่ ...
Alx

6
คุณต้องตั้งค่าreq.session.redirect_to = req.pathภายในมิดเดิลแวร์การตรวจสอบสิทธิ์ของคุณ จากนั้นอ่านและลบในlogin.postเส้นทาง
chovy

นี่ไม่ใช่ทางออกที่ดีสำหรับหนังสือเดินทางที่ยอมรับ successRedirect เป็นตัวเลือก ณ จุดที่คำขอไม่พร้อมใช้งาน
linuxdan

114

ในensureAuthenticatedวิธีการของคุณให้บันทึก url ส่งคืนในเซสชันดังนี้:

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

จากนั้นคุณสามารถอัปเดตหนังสือเดินทางของคุณตรวจสอบสิทธิ์เส้นทางไปยังสิ่งที่ต้องการ:

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

7
ทำงานกับพาสแรก แต่ฉันต้องเพิ่มสิ่งต่อไปนี้หลังจากเปลี่ยนเส้นทางในพาสปอร์ตเส้นทางตรวจสอบสิทธิ์เพื่อหลีกเลี่ยงการถูกนำกลับไปยังที่อยู่ returnTo หลังจากออกจากระบบ / เข้าสู่ระบบในภายหลัง: req.session.returnTo = null;ดูคำถามนี้สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการออกจากระบบเริ่มต้นของหนังสือเดินทางซึ่ง ในการตรวจสอบแหล่งที่มาดูเหมือนว่าจะล้างเฉพาะ session.user เท่านั้นไม่ใช่ทั้งเซสชัน
Rob Andren

ฉันสามารถส่งข้อความค้นหาเช่น? callback = / profile แทน session ได้ไหม
OMGPOP

@OMGPOP คุณอาจทำได้ แต่ฟังดูไม่ค่อยปลอดภัยมีเหตุผลที่คุณไม่ต้องการใช้ประโยชน์จากเซสชันหรือไม่?
linuxdan

@linuxdan ไม่เลย. แต่มันเป็นเรื่องยากที่จะแยกลิงก์เปลี่ยนเส้นทางออกและนำเข้าสู่เซสชันจากนั้นเมื่อผู้ใช้เปลี่ยนเส้นทางกลับให้กำหนดลิงก์เปลี่ยนเส้นทางกลับจากเซสชัน
OMGPOP

5
แทนที่จะreq.pathใช้req.originalUrlเพราะเป็นการรวมกันของ baseUrl และ path ดูเอกสาร
matthaeus


7

วิธีการทำสิ่งต่างๆของฉัน:

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

รับ /ตัวควบคุมการเข้าสู่ระบบ :

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 / logout controller (ไม่แน่ใจว่าใช้ได้ 100% ยินดีรับฟังความคิดเห็น):

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

ล้าง returnTo มิดเดิลแวร์ (ล้าง returnTo จากเซสชันบนเส้นทางใด ๆ ยกเว้นเส้นทาง auth - สำหรับฉันพวกเขาคือ / เข้าสู่ระบบและ / auth /: ผู้ให้บริการ):

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

5

หากคุณใช้การเชื่อมต่อเพื่อให้แน่ใจว่าเข้าสู่ระบบมีวิธีรวมที่ง่ายสุด ๆ ในการดำเนินการนี้กับหนังสือเดินทางโดยใช้successReturnToOrRedirectพารามิเตอร์ เมื่อใช้แล้วหนังสือเดินทางจะส่งคุณกลับไปยัง 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

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

จากนั้นในการเข้าสู่ระบบ

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

ในมุมมองเพิ่มฟิลด์ที่ซ่อนอยู่ในแบบฟอร์มการเข้าสู่ระบบ (ตัวอย่างในหยก)

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

ในการเข้าสู่ระบบ POST

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

โปรดสังเกตว่า redirectTo จะล้างหลังจากเข้าสู่ระบบครั้งแรกด้วย


0

ที่ง่ายที่สุด (และถูกต้อง) วิธีการที่จะบรรลุเป้าหมายนี้คือการตั้งค่าfailureRedirectและsuccessRedirectตัวเลือก


คุณสามารถอธิบายได้หรือไม่? ฉันเห็นว่าsuccessRedirectถูกใช้ในเส้นทางกลับ แต่ปลายทางที่ตั้งใจไว้ (เช่นreturnToด้านบน) จะหายไปใช่ไหม?
Tom Söderlund
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.