จะหมดอายุเซสชั่นเนื่องจากไม่มีการใช้งานใน Django ได้อย่างไร?


97

แอปพลิเคชัน Django ของเรามีข้อกำหนดการจัดการเซสชันดังต่อไปนี้

  1. เซสชันจะหมดอายุเมื่อผู้ใช้ปิดเบราว์เซอร์
  2. เซสชันจะหมดอายุหลังจากไม่มีการใช้งานเป็นระยะเวลาหนึ่ง
  3. ตรวจจับเมื่อเซสชันหมดอายุเนื่องจากไม่มีการใช้งานและแสดงข้อความที่เหมาะสมแก่ผู้ใช้
  4. เตือนผู้ใช้ว่าเซสชันที่กำลังจะมาถึงจะหมดอายุภายในไม่กี่นาทีก่อนสิ้นสุดช่วงเวลาที่ไม่มีการใช้งาน นอกจากคำเตือนแล้วให้ผู้ใช้มีตัวเลือกในการขยายเซสชัน
  5. หากผู้ใช้ทำงานในกิจกรรมทางธุรกิจที่ยาวนานภายในแอปที่ไม่เกี่ยวข้องกับคำขอที่ส่งไปยังเซิร์ฟเวอร์เซสชันจะต้องไม่หมดเวลา

หลังจากอ่านเอกสารโค้ด Django และบล็อกโพสต์บางส่วนที่เกี่ยวข้องกับเรื่องนี้ฉันได้คิดแนวทางการใช้งานต่อไปนี้

ข้อกำหนด 1
ข้อกำหนดนี้สามารถนำไปใช้ได้อย่างง่ายดายโดยตั้งค่า SESSION_EXPIRE_AT_BROWSER_CLOSE เป็น True

ข้อกำหนด 2
ฉันได้เห็นคำแนะนำบางประการในการใช้ SESSION_COOKIE_AGE เพื่อกำหนดระยะเวลาหมดอายุของเซสชัน แต่วิธีนี้มีปัญหาดังต่อไปนี้

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

  • เนื่องจากวิธีการทำงานของคุกกี้ SESSION_EXPIRE_AT_BROWSER_CLOSE และ SESSION_COOKIE_AGE จึงไม่สามารถใช้ร่วมกันได้กล่าวคือคุกกี้จะหมดอายุเมื่อปิดเบราว์เซอร์หรือเมื่อถึงเวลาหมดอายุที่กำหนด หากใช้ SESSION_COOKIE_AGE และผู้ใช้ปิดเบราว์เซอร์ก่อนที่คุกกี้จะหมดอายุคุกกี้จะยังคงอยู่และการเปิดเบราว์เซอร์อีกครั้งจะอนุญาตให้ผู้ใช้ (หรือบุคคลอื่น) เข้าสู่ระบบโดยไม่ต้องตรวจสอบสิทธิ์อีกครั้ง

  • Django อาศัยเฉพาะคุกกี้ที่มีอยู่เพื่อตรวจสอบว่าเซสชันนั้นทำงานอยู่หรือไม่ ไม่ได้ตรวจสอบวันหมดอายุของเซสชันที่เก็บไว้กับเซสชัน

สามารถใช้วิธีการต่อไปนี้เพื่อดำเนินการตามข้อกำหนดนี้และเพื่อแก้ไขปัญหาที่กล่าวถึงข้างต้น

  • อย่าตั้งค่า SESSION_COOKIE_AGE
  • กำหนดวันหมดอายุของเซสชันเป็น 'เวลาปัจจุบัน + ช่วงเวลาที่ไม่มีการใช้งาน' สำหรับทุกคำขอ
  • แทนที่ process_request ใน SessionMiddleware และตรวจสอบการหมดอายุของเซสชัน ทิ้งเซสชันหากหมดอายุแล้ว

ข้อกำหนด 3
เมื่อเราตรวจพบว่าเซสชันหมดอายุ (ใน SessionMiddleware ที่กำหนดเองด้านบน) ให้ตั้งค่าแอตทริบิวต์ตามคำขอเพื่อระบุการหมดอายุของเซสชัน แอตทริบิวต์นี้สามารถใช้เพื่อแสดงข้อความที่เหมาะสมแก่ผู้ใช้

ข้อกำหนด 4
ใช้ JavaScript เพื่อตรวจจับการไม่ใช้งานของผู้ใช้ให้คำเตือนและตัวเลือกในการขยายเซสชัน หากผู้ใช้ต้องการขยายให้ส่งชีพจร Keep alive ไปยังเซิร์ฟเวอร์เพื่อขยายเซสชัน

ข้อกำหนด 5
ใช้ JavaScript เพื่อตรวจจับกิจกรรมของผู้ใช้ (ในระหว่างการดำเนินธุรกิจที่ยาวนาน) และส่งพัลส์ที่ยังมีชีวิตอยู่ไปยังเซิร์ฟเวอร์เพื่อป้องกันไม่ให้เซสชันหมดอายุ


แนวทางการนำไปใช้ข้างต้นดูซับซ้อนมากและฉันสงสัยว่าอาจมีวิธีที่ง่ายกว่านี้ (โดยเฉพาะสำหรับข้อกำหนด 2)

ข้อมูลเชิงลึกใด ๆ จะได้รับการชื่นชมอย่างมาก


3
+1 สำหรับการแก้ปัญหาโดยละเอียด
Don

มีตัวกลางที่อาจทำสิ่งที่คุณต้องการ บน githubและpypi
gbutler

1
"เนื่องจากวิธีการทำงานของคุกกี้ SESSION_EXPIRE_AT_BROWSER_CLOSE และ SESSION_COOKIE_AGE จึงไม่สามารถใช้ร่วมกันได้กล่าวคือคุกกี้จะหมดอายุเมื่อปิดเบราว์เซอร์หรือเมื่อถึงเวลาหมดอายุที่กำหนดหากใช้ SESSION_COOKIE_AGE และผู้ใช้ปิดเบราว์เซอร์ก่อนที่คุกกี้จะหมดอายุคุกกี้จะถูกเก็บไว้และเปิดใหม่ เบราว์เซอร์จะอนุญาตให้ผู้ใช้ (หรือใครก็ตาม) เข้าสู่ระบบโดยไม่ต้องตรวจสอบสิทธิ์อีกครั้ง " แก้ไขฉันถ้าฉันผิด แต่ดูเหมือนจะไม่เป็นจริงอีกต่อไปใน Django เวอร์ชันใหม่ ๆ (อย่างน้อย 1.5+)
Botond Béres

1
"Django อาศัยเฉพาะคุกกี้ที่มีอยู่เพื่อตรวจสอบว่าเซสชันนั้นทำงานอยู่หรือไม่ แต่จะไม่ตรวจสอบวันที่หมดอายุของเซสชันที่เก็บไว้ในเซสชัน" นี่ไม่เป็นความจริงอีกต่อไป
knaperek

ในปี 2020 คำตอบที่สองควรเป็นคำตอบที่ได้รับการยอมรับ สิ่งที่ได้รับการยอมรับในปัจจุบันจะเพิ่มความซับซ้อนให้กับบางสิ่งที่นำมาใช้กับ Django
WhyNotHugo

คำตอบ:


44

นี่คือแนวคิด ... หมดอายุเซสชันบนเบราว์เซอร์โดยปิดด้วยการSESSION_EXPIRE_AT_BROWSER_CLOSEตั้งค่า จากนั้นตั้งเวลาประทับในเซสชันสำหรับทุกคำขอเช่นนั้น

request.session['last_activity'] = datetime.now()

และเพิ่มมิดเดิลแวร์เพื่อตรวจสอบว่าเซสชันหมดอายุหรือไม่ บางอย่างเช่นนี้ควรจัดการกระบวนการทั้งหมด ...

from datetime import datetime
from django.http import HttpResponseRedirect

class SessionExpiredMiddleware:
    def process_request(request):
        last_activity = request.session['last_activity']
        now = datetime.now()

        if (now - last_activity).minutes > 10:
            # Do logout / expire session
            # and then...
            return HttpResponseRedirect("LOGIN_PAGE_URL")

        if not request.is_ajax():
            # don't set this for ajax requests or else your
            # expired session checks will keep the session from
            # expiring :)
            request.session['last_activity'] = now

จากนั้นคุณต้องสร้าง URL และมุมมองเพื่อส่งคืนข้อมูลที่เกี่ยวข้องไปยังการโทรของ ajax เกี่ยวกับการหมดอายุของเซสชัน

เมื่อผู้ใช้เลือกที่จะ "ต่ออายุ" เซสชันดังนั้นในการพูดสิ่งที่คุณต้องทำคือตั้งค่าrequeset.session['last_activity']เวลาปัจจุบันอีกครั้ง

เห็นได้ชัดว่ารหัสนี้เป็นเพียงการเริ่มต้น ... แต่ควรช่วยให้คุณไปถูกทาง


ฉันแค่สงสัยที่นี่ แต่ฉันไม่คิดว่าif not request.is_ajax()จะปลอดภัยอย่างสมบูรณ์ คนที่ถูกระงับการปลอมแปลงเซสชันก่อนหมดอายุ / ส่งการโทร ajax และดำเนินการต่อไปได้หรือไม่?
notbad.jpeg

2
@ notbad.jpeg: โดยทั่วไป "กิจกรรม" สามารถปลอมแปลงได้ง่าย ผู้ที่ถูกระงับเซสชันและส่งคำขอไปเรื่อย ๆ มีการใช้งานอยู่
RemcoGerlich

นี่คือคำตอบที่ยอดเยี่ยม มิดเดิลแวร์เป็นเครื่องมือที่ใช้น้อยมากในการพัฒนา Django
Jamie Counsell

37

ฉันเพิ่งใช้ Django ใหม่

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

ฉันกำลังจะใช้มิดเดิลแวร์ที่กำหนดเองในโค้ดของฉันตามคำตอบยอดนิยมในที่นี่ แต่ฉันก็ยังสงสัยเล็กน้อยเพราะคำตอบที่ดีที่สุดในที่นี่ได้รับการแก้ไขในปี 2011 ฉันใช้เวลามากขึ้นในการค้นหาจากผลการค้นหาล่าสุดและหาวิธีง่ายๆ

SESSION_EXPIRE_AT_BROWSER_CLOSE = True
SESSION_COOKIE_AGE = 10 # set just 10 seconds to test
SESSION_SAVE_EVERY_REQUEST = True

ฉันไม่ได้ตรวจสอบเบราว์เซอร์อื่น แต่เป็น Chrome 1. เซสชันหมดอายุเมื่อฉันปิดเบราว์เซอร์แม้ว่าจะตั้งค่า SESSION_COOKIE_AGE ไว้ก็ตาม 2. เฉพาะเมื่อฉันไม่ได้ใช้งานนานกว่า 10 วินาทีเซสชันจะหมดอายุ ขอบคุณ SESSION_SAVE_EVERY_REQUEST เมื่อใดก็ตามที่คุณเกิดคำขอใหม่มันจะบันทึกเซสชันและการอัปเดตการหมดเวลาที่จะหมดอายุ

หากต้องการเปลี่ยนลักษณะการทำงานเริ่มต้นให้ตั้งค่า SESSION_SAVE_EVERY_REQUEST เป็น True เมื่อตั้งค่าเป็น True Django จะบันทึกเซสชันลงในฐานข้อมูลในทุกคำขอเดียว

โปรดทราบว่าคุกกี้เซสชันจะถูกส่งเมื่อมีการสร้างหรือแก้ไขเซสชันเท่านั้น หาก SESSION_SAVE_EVERY_REQUEST เป็น True คุกกี้เซสชันจะถูกส่งไปทุกคำขอ

ในทำนองเดียวกันส่วนที่หมดอายุของคุกกี้เซสชันจะได้รับการอัปเดตทุกครั้งที่มีการส่งคุกกี้เซสชัน

django คู่มือ 1.10.2

ฉันแค่ทิ้งคำตอบเพื่อให้บางคนที่เป็นคนใหม่ใน Django เหมือนฉันไม่ได้ใช้เวลามากในการหาวิธีแก้ปัญหาเหมือนอย่างที่ฉันทำ


26

django เซสชันความปลอดภัยทำได้แค่นั้น ...

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

Disclamer: ฉันดูแลแอพนี้ แต่ดูกระทู้นี้มานานมากแล้ว :)


1
แอพสุดเจ๋ง - ออกแบบมาอย่างดีและสร้างมาอย่างดี รหัสดีสะอาด ... ขอบคุณ
nicorellius

หากผู้ใช้ปิดเบราว์เซอร์หรือแท็บ (โดยไม่ออกจากระบบ) เมื่อออกจากระบบจะยังบังคับให้ผู้ใช้ออกจากระบบหรือไม่ มันจัดการกับเงื่อนไขนี้หรือไม่?
Mehmet Kagan Kayaalp

ที่จะได้รับการจัดการโดยการหมดอายุของคุกกี้เซสชัน http บริสุทธิ์จะไม่ใช่หรือไม่?
jpic

12

วิธีง่ายๆวิธีหนึ่งในการตอบสนองความต้องการที่สองของคุณคือการตั้งค่า SESSION_COOKIE_AGE ใน settings.py เป็นจำนวนวินาทีที่เหมาะสม ตัวอย่างเช่น:

SESSION_COOKIE_AGE = 600      #10 minutes.

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

request.session.set_expiry(request.session.get_expiry_age())

2
SESSION_COOKIE_AGE = 600 การดำเนินการนี้จะขยายอายุของเซสชันด้วยการร้องขอหน้าใหม่หรือการรีเฟรชหน้าทุกครั้ง
Aseem

1
ฉันยืนยันว่าการตั้งค่าSESSION_COOKIE_AGEก็เพียงพอแล้วและคำขอใด ๆ (การส่งคุกกี้เซสชัน) จะรีเฟรชการหมดอายุของคุกกี้เซสชันโดยอัตโนมัติ
bruno desthuilliers

4

นอกจากนี้คุณยังสามารถใช้ ฟังก์ชัน stackoverflow build in

SESSION_SAVE_EVERY_REQUEST = True

ไม่เกี่ยวข้องทั้งหมด - นี่คือการบังคับให้บันทึกเซสชัน (ไม่ใช่ "การรีเฟรชคุกกี้เซสชัน") ในแต่ละคำขอโดยไม่ตรวจสอบว่ามีการแก้ไขเซสชันหรือไม่
bruno desthuilliers

3

ในคำขอแรกคุณสามารถตั้งค่าการหมดอายุของเซสชันเป็น

self.request.session['access_key'] = access_key
self.request.session['access_token'] = access_token
self.request.session.set_expiry(set_age) #in seconds 

และเมื่อใช้ access_key และโทเค็น

try:
    key = self.request.session['access_key']
except KeyError:
    age = self.request.session.get_expiry_age()
    if age > set_age:
        #redirect to login page
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.