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())
是否有一种更友好的方式来做到这一点?是否有会话/验证中间件设置可以让您将用户名存储为会话表中的列,以便我可以对此进行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
对象,然后一次删除所有会话。 不幸的是,我不知道要过滤会话本身的更直接的方法。
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,并将其存储在具有用户引用的单独模型中。现在,当您要删除用户的所有会话时,只需查询此模型,该模型将返回有关用户的所有活动会话。现在,您需要从会话表中删除这些会话。要比必须查找所有会话要过滤出来的会话要好得多。
可能会有所帮助:
django-password-session
为了在更改密码后使用户的会话无效。由于Django 1.7此功能在开箱即用。 django-admin清除量
# 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')