My Django app特定の条件下で、ユーザー名でユーザーを強制的にログアウトできるようにする必要があります。必ずしも現在ログインしているユーザーではなく、他のユーザーです。ビューには、ログアウトしたいユーザーに関するセッション情報がありません。
私はDjango.authとauth.logoutメソッドに精通していますが、引数としてrequestを取ります。ユーザー名さえあればユーザーをログアウトさせる「ジャンゴウェイ」はありますか?または、自分でログアウトSQLを実行する必要がありますか?
Djangoでこれを行う認可された方法はまだないと思います。
ユーザーIDはセッションオブジェクトに格納されますが、エンコードされます。残念ながら、すべてのセッションを反復処理し、デコードして比較する必要があります...
2つのステップ:
最初に、ターゲットユーザーのセッションオブジェクトを削除します。複数のコンピューターからログインする場合、複数のセッションオブジェクトがあります。
from Django.contrib.sessions.models import Session
from Django.contrib.auth.models import User
# grab the user in question
user = User.objects.get(username='johndoe')
[s.delete() for s in Session.objects.all() if s.get_decoded().get('_auth_user_id') == user.id]
次に、必要に応じてロックアウトします。
user.is_active = False
user.save()
Haroldの答えはこの特定のケースで機能しますが、少なくとも2つの重要な問題が見られます。
Session
モデルは使用されません。これらの問題を解決するには、問題に別のアプローチを取ることをお勧めします。アイデアは、特定のセッションでユーザーがログインした日付と、最後にユーザーにログアウトを要求した日付をどこかに保存することです。
誰かがあなたのサイトにアクセスするたびに、ログイン日がログアウト日よりも低い場合、ユーザーを強制ログアウトできます。ダンが言ったように、ユーザーをすぐにログアウトするか、サイトへの次のリクエストでログアウトするかには、実際的な違いはありません。
次に、Django 1.3b1のこのソリューションの可能な実装を見てみましょう。 3つのステップで:
幸いなことに、Django= authシステムはuser_logged_in
と呼ばれる signal を公開しています。そのシグナルを登録し、セッションの現在の日付を保存するだけです。 models.py
の下部:
from Django.contrib.auth.signals import user_logged_in
from datetime import datetime
def update_session_last_login(sender, user=user, request=request, **kwargs):
if request:
request.session['LAST_LOGIN_DATE'] = datetime.now()
user_logged_in.connect(update_session_last_login)
User
モデルにフィールドとメソッドを追加するだけです。それを達成する方法は複数あります( ユーザープロファイル 、 モデル継承 など)、それぞれ長所と短所があります。
簡単にするために、ここではモデルの継承を使用します。このソリューションを使用する場合は、 カスタム認証バックエンドの作成 を忘れないでください。
from Django.contrib.auth.models import User
from Django.db import models
from datetime import datetime
class MyUser(User):
force_logout_date = models.DateTimeField(null=True, blank=True)
def force_logout(self):
self.force_logout_date = datetime.now()
self.save()
次に、ユーザーjohndoe
のログアウトを強制するには、次の操作を行うだけです。
from myapp.models import MyUser
MyUser.objects.get(username='johndoe').force_logout()
ここでの最善の方法は、ダンが示唆したように ミドルウェア を使用することです。このミドルウェアはrequest.user
にアクセスするため、'Django.contrib.auth.middleware.AuthenticationMiddleware'
設定にafterMIDDLEWARE_CLASSES
を追加する必要があります。
from Django.contrib.auth import logout
class ForceLogoutMiddleware(object):
def process_request(self, request):
if request.user.is_authenticated() and request.user.force_logout_date and \
request.session['LAST_LOGIN_DATE'] < request.user.force_logout_date:
logout(request)
それはそれを行う必要があります。
注
JOIN
が追加されます。ユーザープロファイルを使用すると、追加のクエリが追加されます。 User
を直接変更するのがパフォーマンスの面で最良の方法ですが、それでも 毛深いトピック です。そのソリューションを既存のサイトに展開する場合、おそらく既存のセッションで問題が発生し、'LAST_LOGIN_DATE'
キーはありません。その場合に対処するために、ミドルウェアコードを少し変更することができます。
from Django.contrib.auth import logout
class ForceLogoutMiddleware(object):
def process_request(self, request):
if request.user.is_authenticated() and request.user.force_logout_date and \
( 'LAST_LOGIN_DATE' not in request.session or \
request.session['LAST_LOGIN_DATE'] < request.user.force_logout_date ):
logout(request)
Django 1.2.xでは、user_logged_in
シグナルはありません。login
関数のオーバーライドにフォールバックします。
from Django.contrib.auth import login as dj_login
from datetime import datetime
def login(request, user):
dj_login(request, user)
request.session['LAST_LOGIN_DATE'] = datetime.now()
アプリに似たようなものが必要でした。私の場合、ユーザーが非アクティブに設定されている場合、ユーザーが既にログインしているかどうかを確認し、ログアウトしてサイトを引き続き使用できないようにしました。この投稿を読んだ後、私は次の解決策を見つけました。
from Django.contrib.auth import logout
class ActiveUserMiddleware(object):
def process_request(self, request):
if not request.user.is_authenticated():
return
if not request.user.is_active:
logout(request)
このミドルウェアを設定に追加するだけで、すぐに使用できます。パスワードを変更する場合、userprofileモデルに新しいフィールドを導入して、ユーザーを強制的にログアウトさせ、上記のis_activeの代わりにフィールドの値を確認し、ユーザーがログインしたときにフィールドの設定を解除できます。 Djangoの ser_logged_in シグナルで行われます。
おそらく、ログアウトを強制されたユーザーのリストを参照するミドルウェアのビット。次にユーザーが何かを実行しようとすると、ログアウトしてリダイレクトします。
もちろん、すぐにログアウトする必要がない限り。しかし、再度、彼らはとにかく次にリクエストを行おうとするまで気付かないので、上記の解決策はうまくいくかもしれません。
これは、Balonのクエリへの応答です。
はい、約14万回のセッションを繰り返すことで、Haroldの答えがあなたが望むほど速くないかもしれない理由を見ることができます!
私が推奨する方法は、2つのプロパティのみが外部キーであるモデルをUser
およびSession
オブジェクトに追加することです。次に、このモデルを現在のユーザーセッションで最新の状態に保つミドルウェアを追加します。以前にこの種のセットアップを使用しました。私の場合、これからsessionprofile
モジュールを借りました phpBBのシングルサインオンシステム (「Django/sessionprofile」フォルダーのソースコードを参照)およびこれ(と思います)あなたのニーズに合うでしょう。
最終的には、次のようなコードのどこかに管理機能があります(上記のsessionprofile
モジュールと同じコード名とレイアウトを想定)。
from sessionprofile.models import SessionProfile
from Django.contrib.auth.models import User
# Find all SessionProfile objects corresponding to a given username
sessionProfiles = SessionProfile.objects.filter(user__username__exact='johndoe')
# Delete all corresponding sessions
[sp.session.delete() for sp in sessionProfiles]
(これは、SessionProfile
オブジェクトも削除すると思います。思い出す限り、ForeignKey
によって参照されるオブジェクトが削除されたときのDjangoのデフォルトの動作は、それをカスケード接続し、 ForeignKey
ですが、そうでない場合は、完了時にsessionProfiles
の内容を削除するのは簡単です。
Tony Abou-Assalehとして、非アクティブに設定されたユーザーをログアウトする必要もあったため、彼のソリューションを実装することから始めました。しばらくして、ミドルウェアがすべての要求に対してDBクエリを強制し(ユーザーがブロックされているかどうかを確認するため)、ログインを必要としないページのパフォーマンスが低下することがわかりました。
カスタムユーザーオブジェクトとDjango> = 1.7があるため、最終的には get_session_auth_hash
ユーザーがアクティブでないときにセッションを無効にする機能。可能な実装は次のとおりです。
def get_session_auth_hash(self):
if not self.is_active:
return "inactive"
return super(MyCustomUser, self).get_session_auth_hash()
これが機能するには、Django.contrib.auth.middleware.SessionAuthenticationMiddleware
はsettings.MIDDLEWARE_CLASSES
直接Django関数を使用してそれを行うこともできます。現在のセッションを除く、ユーザーの他のすべてのセッションを更新してログアウトします。
from Django.contrib.auth import update_session_auth_hash
update_session_auth_hash(self.request, user)
他の人が述べたように、DB内のallセッションを繰り返し、すべてのセッションをデコードし、そのユーザーに属するセッションを削除できます。ただし、特にサイトのトラフィックが多く、セッションが多い場合は時間がかかります。
より高速なソリューションが必要な場合は、特定のユーザーのセッションを照会および取得できるセッションバックエンドを使用できます。これらのセッションバックエンドでは、セッションはユーザーへの外部キーを持っているため、allセッションオブジェクトを反復処理する必要はありません。
db
セッションバックエンドに基づく)cached_db
セッションバックエンドに基づく)これらのバックエンドを使用すると、ユーザーのすべてのセッションを削除するのに1行のコードを使用できます。
user.session_set.all().delete()
免責事項:私はDjango-qsessions
の著者です。