本文介绍了删除Django中特定用户所有会话的最优化方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用会话中间件和Auth中间件运行Django 1.3:

I'm running Django 1.3, using Sessions Middleware and Auth Middleware:

# 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或会话到期为止。

Each time a user logs in from a different location (different computer/browser), a new Session() is created and saved with a unique session_id. This can result in multiple database entries for the same user. Their login persists on that node until the cookie is deleted or session expires.

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

When a user changes their password, I want to delete all unexpired sessions for that user from the DB. That way after a password change, they're forced to re-login. This is for security purposes, such as if your computer got stolen, or you accidentally left yourself logged-in on a public terminal.

我想知道最佳的优化方法。这是我的操作方法:

# 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 中看到的那样,我必须将每个未过期的会话从数据库中拉出,即 Session。 objects.filter(expire_date__gte = datetime.datetime.now()),对其全部进行解码,然后检查其是否与用户匹配。如果数据库中存储了超过100,000个会话,这将对数据库造成极大的损失。

As you can see in sessions_helpers.py, I have to pull every unexpired session out of the DB, Session.objects.filter(expire_date__gte=datetime.datetime.now()), decode all of them, and then check to see if it matches a user or not. This will be extremely costly to the database if there are, say, 100,000+ sessions stored in there.

是否有更数据库友好的方法来执行此操作?是否存在会话/身份验证中间件设置,该设置可让您将用户名存储为会话表中的一列,以便我可以针对该用户名运行SQL,还是必须修改会话才能做到这一点?开箱即用,它只有 session_key session_data expire_date 列。

Is there a more-database-friendly way to do this? Is there a Sessions/Auth Middleware setting that'll let you store the username as a column in the Sessions table so I can run SQL against that, or will I have to modify Sessions to do that? Out-of-the-box it only has session_key, session_data, and expire_date columns.

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

Thanks for any insight or help you can offer. :)

推荐答案

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

If you return a QuerySet from your all_unexpired_sessions_for_user function, you could limit your database hits to two:

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

这总共给您带来了两次点击base一次遍历所有 Session 对象,一次遍历所有会话。不幸的是,我不知道一种直接过滤会话本身的更直接的方法。

This gives you a total of two hits to the database. Once to loop over all of the Session objects, and once to delete all of the sessions. Unfortunately, I don't know of a more direct way to filter through the sessions themselves.

这篇关于删除Django中特定用户所有会话的最优化方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-28 04:19
查看更多