最优化的方法来删除Django中特定用户的所有会话? 我正在运行Django 1.3,使用Sessions Muiddrware和Auth Middleware: #settings.py session_engine = django.contrib.sessions.backends.db#persist sessions to db session_cookie_age = 1209600 ...

问题描述 投票:0回答:5
用户从其他位置(不同的计算机/浏览器)登录,创建了一个新的

Session()

使用唯一的
session_id
创建并保存。这可能会导致同一用户的多个数据库条目。他们的登录名在该节点上一直存在,直到删除cookie或会话到期为止。

当用户更改密码时,我想从数据库中删除该用户的所有未满会话。这样,在更改密码后,它们被迫重新使用。这是出于安全目的,例如您的计算机被盗,或者您不小心将自己登录在公共终端上。 我想知道优化这一点的最佳方法。

# sessions_helpers.py

from django.contrib.sessions.models import Session
import datetime

def all_unexpired_sessions_for_user(user):
    user_sessions = []
    all_sessions  = Session.objects.filter(expire_date__gte=datetime.datetime.now())
    for session in all_sessions:
        session_data = session.get_decoded()
        if user.pk == session_data.get('_auth_user_id'):
            user_sessions.append(session)
    return user_sessions

def delete_all_unexpired_sessions_for_user(user, session_to_omit=None):
    for session in all_unexpired_sessions_for_user(user):
        if session is not session_to_omit:
            session.delete()
非常简化的视图:

# views.py from django.http import HttpResponse from django.shortcuts import render_to_response from myapp.forms import ChangePasswordForm from sessions_helpers import delete_all_unexpired_sessions_for_user @never_cache @login_required def change_password(request): user = request.user if request.method == 'POST': form = ChangePasswordForm(data=request) if form.is_valid(): user.set_password(form.get('password')) user.save() request.session.cycle_key() # Flushes and replaces old key. Prevents replay attacks. delete_all_unexpired_sessions_for_user(user=user, session_to_omit=request.session) return HttpResponse('Success!') else: form = ChangePasswordForm() return render_to_response('change_password.html', {'form':form}, context_instance=RequestContext(request))

您可以在

sessions_helpers.py中看到,我必须将每个未期的会话从DB,Session.objects.filter(expire_date__gte=datetime.datetime.now())

,解码所有会话中,然后检查它是否与用户匹配。如果在那里存储了100,000多个会话,则数据库将非常昂贵。
是否有一种更友好的方式来做到这一点?是否有会话/验证中间件设置可以让您将用户名存储为会话表中的列,以便我可以对此进行SQL,或者我必须修改会话才能做到这一点?开箱即用,只有
session_key

session_data
expire_date

列。

感谢您提供的任何见解或帮助。 :)
	

如果您从

all_unexpired_sessions_for_user

函数返回QuerySet,则可以将数据库命中限制在两个:
from django.utils import timezone

def all_unexpired_sessions_for_user(user):
    user_sessions = []
    all_sessions  = Session.objects.filter(expire_date__gte=timezone.now())
    for session in all_sessions:
        session_data = session.get_decoded()
        if user.pk == int(session_data.get('_auth_user_id')):
            user_sessions.append(session.pk)
    return Session.objects.filter(pk__in=user_sessions)

def delete_all_unexpired_sessions_for_user(user, session_to_omit=None):
    session_list = all_unexpired_sessions_for_user(user)
    if session_to_omit is not None:
        session_list.exclude(session_key=session_to_omit.session_key)
    session_list.delete()
这为您提供了数据库的两次命中。  一旦循环循环到所有
Session

对象,然后一次删除所有会话。 不幸的是,我不知道要过滤会话本身的更直接的方法。

python django session-cookies django-authentication django-sessions
5个回答
30
投票

from django.utils import timezone from django.contrib.sessions.models import Session def delete_all_unexpired_sessions_for_user(user): unexpired_sessions = Session.objects.filter(expire_date__gte=timezone.now()) [ session.delete() for session in unexpired_sessions if str(user.pk) == session.get_decoded().get('_auth_user_id') ]

    

最有效的方法是在登录过程中存储用户的会话ID。您可以使用request.session._session_key访问会话ID,并将其存储在具有用户引用的单独模型中。现在,当您要删除用户的所有会话时,只需查询此模型,该模型将返回有关用户的所有活动会话。现在,您需要从会话表中删除这些会话。要比必须查找所有会话要过滤出来的会话要好得多。 

可能会有所帮助:


8
投票

django-password-session

为了在更改密码后使用户的会话无效。由于Django 1.7此功能在开箱即用。
django-admin清除量

2
投票


1
投票
这不是直接的答案,而是解决您的问题并将DB命中率降低到零。使用Django的最新版本,您可以使用基于cookie的后端:

# in model.py a regular User model from django.contrib.postgres.fields import ArrayField class User(AbstractUser): # other fields # This could be a JsonField to store other data of logedin user # like IP or Agent to have more control on users logout session_keys = ArrayField(models.CharField(max_length=255), default=list) # in views.py a simple login view def login(request): form = LoginForm(request.POST or None, request=request) if form.is_valid(): form.save() return redirect(request.GET.get('next')) context = { 'form': form, 'next': request.GET.get('next'), } return render(request, 'register.html', context) # in forms.py a form that check regular password and user name checks class LoginForm(forms.Form): username = forms.CharField(required=True) password = forms.CharField(required=True) def __init__(self, *args, **kwargs): self.request = kwargs.pop('request', None) super().__init__(*args, **kwargs) def clean(self): # some check def save(self): # create a session for user # I had multiple backend if you have one session backend # there is no need to provide it login(self.request, self.user, backend='django.contrib.auth.backends.ModelBackend') # if everything be ok after creating session, login # function will add created session instance to request # object as a property and we can find its key # (it is little complicated then what I said...) self.user.session_keys.append(self.request.session.session_key) self.user.save() # then again in views.py from django.contrib.sessions.models import Session def logout(request): user = self.request.user Session.objects.filter(session_key__in=user.session_keys).delete() user.session_keys = [] user.save() return render(request, 'logout.html')

0
投票

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.