24

Sessions Middleware と Auth Middleware を使用して、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 が削除されるか、セッションが期限切れになるまで、そのノードに保持されます。

ユーザーがパスワードを変更すると、そのユーザーの有効期限が切れていないすべてのセッションを DB から削除したいと考えています。そうすれば、パスワードの変更後、再ログインが強制されます。これは、コンピューターが盗難にあった場合や、誤って公衆端末にログインしたままにした場合などのセキュリティ上の目的のためです。

これを最適化する最良の方法を知りたいです。これが私がやった方法です:

# 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 以上のセッションが保存されている場合、これはデータベースにとって非常にコストがかかります。

これを行うためのよりデータベースに適した方法はありますか? ユーザー名をSessionsテーブルの列として保存できるようにするSessions / Auth Middleware設定はありますか?それに対してSQLを実行できますか?それを行うにはSessionsを変更する必要がありますか? すぐに使用できるのはsession_key、、、session_dataおよびexpire_date列のみです。

あなたが提供できる洞察や助けに感謝します。:)

4

6 に答える 6

27

関数からQuerySetを返す場合all_unexpired_sessions_for_user、データベースのヒット数を2つに制限できます。

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

これにより、データベースに合計2つのヒットが与えられます。1回はすべてのオブジェクトをループしSession、もう1回はすべてのセッションを削除します。残念ながら、セッション自体をフィルタリングするためのより直接的な方法はわかりません。

于 2011-07-11T21:47:49.353 に答える
2

最も効率的な方法は、ログイン時にユーザーのセッション ID を保存することです。request.session._session_key を使用してセッション ID にアクセスし、ユーザーを参照する別のモデルに保存できます。ユーザーのすべてのセッションを削除したい場合は、このモデルをクエリするだけで、問題のユーザーのアクティブなセッションがすべて返されます。ここで、これらのセッションのみをセッション テーブルから削除する必要があります。特定のユーザーのセッションだけを除外するためにすべてのセッションを検索するよりもはるかに優れています。

于 2013-07-02T17:01:26.460 に答える
0

これは直接的な答えではありませんが、問題を解決し、DB ヒットをゼロに減らします。最近のバージョンの Django では、Cookie ベースのセッション バックエンドを使用できます。

https://docs.djangoproject.com/en/dev/topics/http/sessions/#cookie-session-backend

于 2014-10-29T07:48:11.047 に答える