Django: báo hiệu khi người dùng đăng nhập?


82

Trong ứng dụng Django của mình, tôi cần bắt đầu chạy một số công việc nền định kỳ khi người dùng đăng nhập và ngừng chạy chúng khi người dùng đăng xuất, vì vậy tôi đang tìm một cách đơn giản để

  1. nhận được thông báo về người dùng đăng nhập / đăng xuất
  2. truy vấn trạng thái đăng nhập của người dùng

Theo quan điểm của tôi, giải pháp lý tưởng sẽ là

  1. một tín hiệu được gửi bởi mỗi django.contrib.auth.views.login... views.logout
  2. một phương pháp django.contrib.auth.models.User.is_logged_in(), tương tự với ... User.is_active()hoặc... User.is_authenticated()

Django 1.1.1 không có điều đó và tôi miễn cưỡng vá nguồn và thêm nó (dù sao cũng không chắc chắn về cách làm điều đó).

Như một giải pháp tạm thời, tôi đã thêm is_logged_intrường boolean vào mô hình UserProfile được xóa theo mặc định, được đặt lần đầu tiên người dùng truy cập trang đích (được xác định bởi LOGIN_REDIRECT_URL = '/') và được truy vấn trong các yêu cầu tiếp theo. Tôi đã thêm nó vào UserProfile, vì vậy tôi không phải bắt nguồn và tùy chỉnh mô hình Người dùng nội trang chỉ cho mục đích đó.

Tôi không thích giải pháp này. Nếu người dùng nhấp vào nút đăng xuất một cách rõ ràng, tôi có thể xóa cờ, nhưng hầu hết thời gian, người dùng chỉ rời khỏi trang hoặc đóng trình duyệt; xóa cờ trong những trường hợp này dường như không thẳng với tôi. Bên cạnh đó (mặc dù vậy, is_logged_inđó là sự rõ ràng của mô hình dữ liệu), không thuộc về UserProfile, mà là trong mô hình Người dùng.

Có ai có thể nghĩ ra các cách tiếp cận thay thế không?


4
Vui lòng xem xét chọn một câu trả lời mới. Sự lựa chọn hiện tại được chấp nhận là một sự lựa chọn rất tồi theo tín hiệu được thêm vào trong 1.3.
Bryson

1
Bạn đúng; đã thay đổi câu trả lời được chấp nhận.
ssc

Câu trả lời:


151

Bạn có thể sử dụng một tín hiệu như thế này (tôi đã đặt của tôi trong models.py)

from django.contrib.auth.signals import user_logged_in


def do_stuff(sender, user, request, **kwargs):
    whatever...

user_logged_in.connect(do_stuff)

Xem tài liệu django: https://docs.djangoproject.com/en/dev/ref/contrib/auth/#module-django.contrib.auth.signals và tại đây http://docs.djangoproject.com/en/dev/ chủ đề / tín hiệu /


8
Bây giờ Django 1.3 cung cấp các tín hiệu này, đó là một giải pháp tốt hơn nhiều so với gói lệnh gọi đăng nhập / đăng xuất. Điều đó cũng có nghĩa là nếu bạn thiết lập các cách đăng nhập mới - ví dụ: đăng nhập Facebook / Twitter / OpenID - chúng sẽ vẫn hoạt động.
Jordan Reiter,

9
Thay vào đó, hãy đặt nó vào models.pythay vào đó, tôi khuyên bạn nên đặt mã vào signals.pyvà tự động nhập mã đó vào __init__.pytệp của mô-đun .
Daniel Sokolowski

làm thế nào để sử dụng tín hiệu này để chạy javascript khi đăng nhập và đăng xuất?
Ashish Gupta

15

Ngoài câu trả lời @PhoebeB: bạn cũng có thể sử dụng trình @receivertrang trí như thế này:

from django.contrib.auth.signals import user_logged_in
from django.dispatch import receiver

@receiver(user_logged_in)
def post_login(sender, user, request, **kwargs):
    ...do your stuff..

Và nếu bạn đưa nó vào signals.pytrong ứng dụng của mình, thì hãy thêm nó vào apps.py:

class AppNameConfig(AppConfig):
    ...
    def ready(self):
        import app_name.signals

13

Một tùy chọn có thể là bao gồm các chế độ xem đăng nhập / đăng xuất của Django với của riêng bạn. Ví dụ:

from django.contrib.auth.views import login, logout

def my_login(request, *args, **kwargs):
    response = login(request, *args, **kwargs)
    #fire a signal, or equivalent
    return response

def my_logout(request, *args, **kwargs):
    #fire a signal, or equivalent
    return logout(request, *args, **kwargs)

Sau đó, bạn sử dụng các chế độ xem này trong mã của mình thay vì của Django, và thì đấy.

Liên quan đến truy vấn trạng thái đăng nhập, khá đơn giản nếu bạn có quyền truy cập vào đối tượng yêu cầu; chỉ cần kiểm tra thuộc tính người dùng của yêu cầu để xem họ là người dùng đã đăng ký hay người dùng ẩn danh và chơi lô tô. Để trích dẫn tài liệu Django :

if request.user.is_authenticated():
    # Do something for logged-in users.
else:
    # Do something for anonymous users.

Nếu bạn không có quyền truy cập vào đối tượng yêu cầu, thì việc xác định xem người dùng hiện tại có đăng nhập hay không sẽ rất khó.

Biên tập:

Thật không may, bạn sẽ không bao giờ có thể có được User.is_logged_in()chức năng - đó là một hạn chế của giao thức HTTP. Tuy nhiên, nếu bạn đưa ra một vài giả định, bạn có thể đạt được những gì bạn muốn.

Đầu tiên, tại sao bạn không thể có được chức năng đó? Chà, bạn không thể phân biệt được sự khác biệt giữa ai đó đang đóng trình duyệt hay ai đó dành thời gian trên một trang trước khi tìm nạp một trang mới. Không có cách nào để biết qua HTTP khi ai đó thực sự rời khỏi trang web hoặc đóng trình duyệt.

Vì vậy, bạn có hai lựa chọn ở đây không hoàn hảo:

  1. Sử dụng unloadsự kiện của Javascript để nắm bắt thời điểm người dùng rời khỏi trang. Tuy nhiên, bạn phải viết một số logic cẩn thận để đảm bảo rằng bạn không đăng xuất người dùng khi họ vẫn đang điều hướng trang web của bạn .
  2. Kích hoạt tín hiệu đăng xuất bất cứ khi nào người dùng đăng nhập, chỉ để chắc chắn. Ngoài ra, hãy tạo một công việc cron chạy khá thường xuyên để loại bỏ các phiên đã hết hạn - khi một phiên hết hạn bị xóa, hãy kiểm tra xem người dùng của phiên đó (nếu không phải là ẩn danh) không còn phiên hoạt động nào nữa, trong trường hợp đó bạn kích hoạt tín hiệu đăng xuất.

Những giải pháp này rất lộn xộn và không lý tưởng, nhưng thật không may, chúng là cách tốt nhất bạn có thể làm.


Điều này vẫn không giải quyết được tình trạng "hầu hết thời gian, người dùng chỉ rời khỏi trang hoặc đóng trình duyệt".
Joel L

Cảm ơn câu trả lời của bạn, tất nhiên gói đăng nhập / đăng xuất giúp tôi không phải vá nguồn, bản thân tôi nên có. Tuy nhiên, cần sửa một chút: Nếu tín hiệu được gửi trong my_login trước khi đăng nhập được gọi, người dùng vẫn ẩn danh trong trình xử lý tín hiệu. Tốt hơn (đừng nghĩ rằng điều này sẽ được định dạng đúng): def my_login (request): response = login (request) #fire a signal, hoặc phản hồi trả lại tương đương Ngoài ra, tôi đang sử dụng is_authenticated rồi, tôi chỉ có cảm giác rằng tôi sẽ cần nhiều hơn thế. Tuy nhiên, cho đến nay, phần is_logged_in mới của tôi trong mô hình dữ liệu vẫn chưa được sử dụng.
ssc

Những điểm tốt xung quanh. Tôi đoán tôi đã không thực sự giải quyết vấn đề is_logged_intốt chút nào (xin lỗi ở đó, tôi đoán tôi đã không làm tốt việc đọc bài đăng), nhưng tôi đã cập nhật câu trả lời để cung cấp những gì tôi có thể giúp đỡ trong lĩnh vực đó . Thật không may, đó là một vấn đề không thể xảy ra.
ShZ

3

một giải pháp nhanh chóng cho điều này sẽ là, trong _ _ init _ _.py của ứng dụng của bạn, đặt mã sau:

from django.contrib.auth.signals import user_logged_in
from django.dispatch import receiver


@receiver(user_logged_in)
def on_login(sender, user, request, **kwargs):
    print('User just logged in....')

1

Cách đáng tin cậy duy nhất (cũng có thể phát hiện khi người dùng đã đóng trình duyệt) là cập nhật một số last_request trường mỗi khi người dùng tải một trang.

Bạn cũng có thể có một yêu cầu AJAX định kỳ ping máy chủ x phút một lần nếu người dùng mở một trang.

Sau đó, có một công việc nền duy nhất lấy danh sách những người dùng gần đây, tạo công việc cho họ và xóa công việc cho những người dùng không có trong danh sách đó.


Đây là cách tốt nhất để đo lường xem người dùng có đăng nhập hay không. Về cơ bản, bạn sẽ phải giữ một danh sách người dùng đã đăng nhập và kiểm tra xem lần truy cập cuối cùng của họ là gì và quyết định thời gian chờ. Nếu bạn đặt thời gian chờ là 10 phút và sau đó mỗi trang web gọi một yêu cầu Ajax cứ sau 5 phút hoặc lâu hơn trong khi một trang đang hoạt động, điều đó sẽ giữ trạng thái được cập nhật.
Jordan Reiter

1

Suy ra đăng xuất, trái ngược với việc họ nhấp vào nút một cách rõ ràng (mà không ai làm), có nghĩa là chọn một lượng thời gian nhàn rỗi tương đương với "đã đăng xuất". phpMyAdmin sử dụng mặc định là 15 phút, một số trang ngân hàng sử dụng ít nhất là 5 phút.

Cách đơn giản nhất để thực hiện điều này là thay đổi thời gian tồn tại của cookie. Bạn có thể làm điều này cho toàn bộ trang web của mình bằng cách chỉ định settings.SESSION_COOKIE_AGE. Ngoài ra, bạn có thể thay đổi nó trên cơ sở mỗi người dùng (dựa trên một số bộ tiêu chí tùy ý) bằng cách sử dụng HttpResponse.setcookie(). Bạn có thể tập trung mã này bằng cách tạo phiên bản của riêng bạn render_to_response()và để nó đặt thời gian tồn tại cho mỗi phản hồi.


0

Ý tưởng sơ sài - bạn có thể sử dụng phần mềm trung gian cho việc này. Phần mềm trung gian này có thể xử lý các yêu cầu và báo hiệu kích hoạt khi URL có liên quan được yêu cầu. Nó cũng có thể xử lý phản hồi và tín hiệu báo cháy khi hành động được đưa ra thực sự không thành công.

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.