programing

Django에서 사용자 로그 아웃을 강제하는 방법은 무엇입니까?

nasanasas 2020. 10. 31. 09:46
반응형

Django에서 사용자 로그 아웃을 강제하는 방법은 무엇입니까?


특정 조건에서 내 django 앱에서 사용자 이름으로 사용자 로그 아웃을 강제 할 수 있어야합니다. 로그인 한 현재 사용자 일 필요는 없지만 다른 사용자입니다. 따라서 내 뷰의 요청 메서드에는 로그 아웃하려는 사용자에 대한 세션 정보가 없습니다.

django.auth와 auth.logout 메서드에 익숙하지만 요청 을 인수로받습니다. 사용자 이름 만 있으면 사용자를 로그 아웃하는 "django-way"가 있습니까? 아니면 내 로그 아웃 SQL을 롤아웃해야합니까?


아직 장고에서 이것을 할 수있는 승인 된 방법이 없다고 생각합니다.

사용자 ID는 세션 개체에 저장되지만 인코딩됩니다. 불행히도 모든 세션을 반복하고 디코딩하고 비교해야 함을 의미합니다.

두 단계 :

먼저 대상 사용자의 세션 개체를 삭제하십시오. 여러 컴퓨터에서 로그인하면 여러 세션 개체를 갖게됩니다.

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]

그런 다음 필요한 경우 잠그십시오 ....

user.is_active = False
user.save()

이 특정 경우에 Harold의 대답이 작동하지만 적어도 두 가지 중요한 문제를 볼 수 있습니다.

  1. 이 솔루션은 데이터베이스 세션 엔진 에서만 사용할 수 있습니다 . 다른 상황 (캐시, 파일, 쿠키)에서는 Session모델이 사용되지 않습니다.
  2. 데이터베이스의 세션 및 사용자 수가 증가하면 이는 매우 비효율적입니다.

이러한 문제를 해결하려면 문제에 대해 다른 접근 방식을 취하는 것이 좋습니다. 아이디어는 사용자가 특정 세션에 로그인 한 날짜와 마지막으로 사용자에게 로그 아웃을 요청한 날짜를 저장하는 것입니다.

그런 다음 누군가 사이트에 액세스 할 때마다 로그인 날짜가 로그 아웃 날짜 보다 낮 으면 사용자를 강제 로그 아웃 할 수 있습니다. Dan이 말했듯이 사용자를 즉시 ​​로그 아웃하거나 사이트에 대한 다음 요청에서 로그 아웃하는 것 사이에는 실질적인 차이가 없습니다.

이제 django 1.3b1에 대한이 솔루션의 가능한 구현을 살펴 보겠습니다 . 3 단계 :

1. 마지막 로그인 날짜를 세션에 저장

다행히, 장고 인증 시스템은 노출 신호 라는를 user_logged_in. 해당 신호를 등록하고 세션에 현재 날짜를 저장하기 만하면됩니다. 귀하의 하단에 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. 사용자에 대한 강제 로그 아웃 요청

User모델에 필드와 메서드를 추가하기 만하면 됩니다. 이를 달성하는 방법에는 여러 가지가 있습니다 ( 사용자 프로필 , 모델 상속 등) 각각 장단점이 있습니다.

단순함을 위해 여기서 모델 상속을 사용할 것입니다.이 솔루션을 사용 하는 경우 사용자 지정 인증 백엔드작성하는 것을 잊지 마십시오 .

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

그런 다음 user johndoe대해 강제 로그 아웃하려면 다음을 수행하면 됩니다.

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

3. 액세스 확인 구현

여기서 가장 좋은 방법은 dan이 제안한 미들웨어 를 사용하는 것 입니다. 이 미들웨어는에 액세스 request.user하므로 설정 에서 뒤에 넣어야 합니다.'django.contrib.auth.middleware.AuthenticationMiddleware'MIDDLEWARE_CLASSES

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)

그렇게해야합니다.


메모

  • 사용자를 위해 추가 필드를 저장하는 것이 성능에 미치는 영향을 인식하십시오. 모델 상속을 사용하면 JOIN. 사용자 프로필을 사용하면 추가 쿼리가 추가됩니다. 직접 수정하는 User것이 성능 측면에서 가장 좋은 방법이지만 여전히 어려운 주제 입니다.
  • 해당 솔루션을 기존 사이트에 배포하는 경우 'LAST_LOGIN_DATE'가없는 기존 세션에 문제가있을 수 있습니다 . 이 경우를 처리하기 위해 미들웨어 코드를 약간 조정할 수 있습니다.

    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)
    
  • django 1.2.x에는 user_logged_in신호 가 없습니다 . login함수 재정의로 돌아가십시오 .

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

I needed something similar in my app. In my case, if a user was set to inactive, I wanted to make sure if the user was already logged in that they will be logged out and not able to continue to use the site. After reading this post, I came to the following solution:

from django.contrib.auth import logout

class ActiveUserMiddleware(object):
    def process_request(self, request):
        if not request.user.is_authenticated():
            return
        if not request.user.is_active:
           logout(request)

Just add this middleware in your settings and off you go. In the case of changing passwords, you could introduce a new field in the userprofile model that forces a user to logout, check for the value of the field instead of is_active above, and also unset the field when a user logs in. The latter can be done with Django's user_logged_in signal.


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.


This is in response to Balon's query:

Yes, with around 140k sessions to iterate through I can see why Harold's answer may not be as fast as you may like!

The way I would recommend is to add a model whose only two properties are foreign keys to User and Session objects. Then add some middleware that keeps this model up-to-date with current user sessions. I have used this sort of setup before; in my case, I borrowed the sessionprofile module from this Single Sign-On system for phpBB (see the source code in the "django/sessionprofile" folder) and this (I think) would suit your needs.

What you would end up with is some management function somewhere in your code like this (assuming the same code names and layout as in the sessionprofile module linked above):

from sessionprofile.models import SessionProfile
from django.contrib.auth.models import User

# Find all SessionProfile objects corresponding to a given username
sessionProfiles = SessionProfile.objects.filter(user__username__exact='johndoe')

# Delete all corresponding sessions
[sp.session.delete() for sp in sessionProfiles]

(I think this will also delete the SessionProfile objects, as from what I recall, Django's default behaviour when an object referenced by a ForeignKey is deleted is to cascade it and also delete the object containing the ForeignKey, but if not then it is trivial enough to delete the contents of sessionProfiles when you are done.)


As Tony Abou-Assaleh, I also needed to log out users who were set to inactive, so I started by implementing his solution. After some time I found out that the middleware is forcing a DB query on all requests (to check if the user was blocked), and thus hurts performance on pages that doesn't require login.

I have a custom user object and Django >= 1.7, so what I ended up doing is overriding its get_session_auth_hash function to invalidate the session when the user is inactive. A possible implementation is:

def get_session_auth_hash(self):
    if not self.is_active:
        return "inactive"
    return super(MyCustomUser, self).get_session_auth_hash()

For this to work, django.contrib.auth.middleware.SessionAuthenticationMiddleware should be in settings.MIDDLEWARE_CLASSES


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)

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:

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.


from django.contrib.sessions.models import Session

deleting user session

[s.delete() for s in Session.objects.all() if s.get_decoded().get('_auth_user_hash') == user.get_session_auth_hash()]

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

참고URL : https://stackoverflow.com/questions/953879/how-to-force-user-logout-in-django

반응형