How to force a user logout in Django?

前端 未结 10 2138
粉色の甜心
粉色の甜心 2020-11-30 17:27

In my Django app under certain conditions I want to be able to force users to log out by a username. Not necessarily the current user who is logged in, but another user. So,

相关标签:
10条回答
  • 2020-11-30 18:03

    You can also use direct django function to do that, it will update and logs out all other sessions for the user, except the current one.

    from django.contrib.auth import update_session_auth_hash
    update_session_auth_hash(self.request, user)
    

    Docs for update_session_auth_hash here.

    0 讨论(0)
  • 2020-11-30 18:12

    Even I faced this issue. Few spammers from India keep posting about those Baba and Molvi for love solutions.

    What I did is at the time of posting just inserted this code:

    if request.user.is_active==False:
                return HttpResponse('You are banned on the site for spaming.')
    
    0 讨论(0)
  • 2020-11-30 18:13

    I don't think there is a sanctioned way to do this in Django yet.

    The user id is stored in the session object, but it is encoded. Unfortunately, that means you'll have to iterate through all sessions, decode and compare...

    Two steps:

    First delete the session objects for your target user. If they log in from multiple computers they will have multiple session objects.

    from django.contrib.sessions.models import Session
    from django.contrib.auth.models import User
    
    # grab the user in question 
    user = User.objects.get(username='johndoe')
    
    [s.delete() for s in Session.objects.all() if s.get_decoded().get('_auth_user_id') == user.id]
    

    Then, if you need to, lock them out....

    user.is_active = False
    user.save()
    
    0 讨论(0)
  • 2020-11-30 18:14

    As others stated, you can iterate over all sessions in DB, decode all of them, and delete those belonging to that user. But it's slow, particularly if your site has high traffic and there are lots of sessions.

    If you need a faster solution, you can use a session backend that lets you query and get the sessions of a specific user. In these session backends, Session has a foreign key to User, so you don't need to iterate over all session objects:

    • django-qsessions (based on django's db, cached_db session backends)
    • django-user-sessions (based on django's db session backend)

    Using these backends, deleting all sessions of a user can be done in a single line of code:

    user.session_set.all().delete()
    

    Disclaimer: I am the author of django-qsessions.

    0 讨论(0)
  • 2020-11-30 18:16

    Although Harold's answer works in this specific case, I can see at least two important issues with it:

    1. This solution can only be used with a database session engine. In other situations (cache, file, cookie) the Session model would not be used.
    2. When the number of sessions and users in database grows, this becomes quite inefficient.

    To solve those issues, I suggest you take another approach at the problem. The idea is to store somewhere the date when the user was logged in for a given session, and the last time you requested an user to be logged out.

    Then whenever someone access your site, if the logged in date is lower than the log out date, you can force-logout the user. As dan said, there's no practical difference between logging out an user immediately or on his next request to your site.

    Now, let's see a possible implementation of this solution, for django 1.3b1. In three steps:

    1. store in the session the last login date

    Fortunately, Django auth system exposes a signal called user_logged_in. You just have to register that signals, and save the current date in the session. At the bottom of your models.py :

    from django.contrib.auth.signals import user_logged_in
    from datetime import datetime
    
    def update_session_last_login(sender, user=user, request=request, **kwargs):
        if request:
            request.session['LAST_LOGIN_DATE'] = datetime.now()
    user_logged_in.connect(update_session_last_login)
    

    2. request a force logout for an user

    We just need to add a field and a method to the User model. There's multiple ways to achieve that (user profiles, model inheritance, etc.) each with pros and cons.

    For the sake of simplicity, I'm gonna use model inheritance here, if you go for this solution, don't forget to write a custom authentication backend.

    from django.contrib.auth.models import User
    from django.db import models
    from datetime import datetime
    
    class MyUser(User):
        force_logout_date = models.DateTimeField(null=True, blank=True)
    
        def force_logout(self):
            self.force_logout_date = datetime.now()
            self.save()
    

    Then, if you want to force logout for user johndoe, you just have to:

    from myapp.models import MyUser
    MyUser.objects.get(username='johndoe').force_logout()
    

    3. implement the check on access

    Best way here is to use a middleware as dan suggested. This middleware will access request.user, so you need to put it after 'django.contrib.auth.middleware.AuthenticationMiddleware' in your MIDDLEWARE_CLASSES setting.

    from django.contrib.auth import logout
    
    class ForceLogoutMiddleware(object):
        def process_request(self, request):
            if request.user.is_authenticated() and request.user.force_logout_date and \
               request.session['LAST_LOGIN_DATE'] < request.user.force_logout_date:
                logout(request)
    

    That should do it.


    Notes

    • Be aware of the performance implication of storing an extra field for your users. Using model inheritance will add an extra JOIN. Using user profiles will add an extra query. Modifying directly the User is the best way performance wise, but it is still a hairy topic.
    • If you deploy that solution on an existing site, you will probably have some trouble with existing sessions, which won't have the 'LAST_LOGIN_DATE' key. You can adapt a bit the middleware code to deal with that case :

      from django.contrib.auth import logout
      
      class ForceLogoutMiddleware(object):
          def process_request(self, request):
              if request.user.is_authenticated() and request.user.force_logout_date and \
                 ( 'LAST_LOGIN_DATE' not in request.session or \
                   request.session['LAST_LOGIN_DATE'] < request.user.force_logout_date ):
                  logout(request)
      
    • In django 1.2.x, there is no user_logged_in signal. Fall back to overriding the login function:

      from django.contrib.auth import login as dj_login
      from datetime import datetime
      
      def login(request, user):
          dj_login(request, user)
          request.session['LAST_LOGIN_DATE'] = datetime.now()
      
    0 讨论(0)
  • 2020-11-30 18:16

    Perhaps, a bit of middleware that references a list of users who have been forced to log out. Next time the user tries to do anything, log them out then, redirects them, etc.

    Unless of course, they need to be logged out immediately. But then again, they wouldn't notice until they next tried to make a request anyway, so the above solution may just work.

    0 讨论(0)
提交回复
热议问题