我正在使用 session 中间件和身份验证中间件运行Django 1.3:

# settings.py

SESSION_ENGINE = django.contrib.sessions.backends.db   # Persist sessions to DB
SESSION_COOKIE_AGE = 1209600                           # Cookies last 2 weeks

每次用户从其他位置(不同的计算机/浏览器)登录时,都会创建一个新的Session()并使用唯一的session_id保存。这可能导致同一用户有多个数据库条目。他们的登录将继续在该节点上,直到cookie被删除或 session 到期为止。

当用户更改密码时,我想从数据库中删除该用户的所有未到期的 session 。这样,更改密码后,他们将被迫重新登录。这是出于安全目的,例如您的计算机被盗,或者您不小心使自己登录了公共(public)终端。

我想知道优化此方法的最佳方法。 这是我的做法:
# 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()

一个非常简化的 View :
# 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中看到的那样,我必须将每个未过期的 session 拉出数据库Session.objects.filter(expire_date__gte=datetime.datetime.now()),解码所有 session ,然后检查它是否与用户匹配。如果数据库中存储了100,000多个 session ,这将对数据库造成极大的损失。

有没有更数据库友好的方式来做到这一点?是否存在“ session /身份验证中间件”设置,该设置可让您将用户名存储为“ session ”表中的一列,这样我就可以对它运行SQL,还是必须修改 session 才能做到这一点?开箱即用的只有session_keysession_dataexpire_date列。

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

最佳答案

如果从all_unexpired_sessions_for_user函数返回QuerySet,则可以将数据库命中次数限制为两个:

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.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对象,一次循环删除所有 session 。不幸的是,我不知道一种直接过滤 session 本身的更直接的方法。

关于python - 删除Django中特定用户所有 session 的最优化方法?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/6656708/

10-09 18:05
查看更多