Verwenden Sie RemoteUserMiddleware, um SSO mit Django durchzuführen.
Auf der Django-App-Seite wird der Benutzer anhand der Umgebungsvariablen REMOTE_USER identifiziert, die vom SAML-Modul (Shibboleth in der eigenen Umgebung) als ID-Informationen festgelegt wurde. Zu diesem Zeitpunkt sind jedoch auch andere Attribute als die ID-Informationen (z. B. E-Mail) in der Benutzerklasse auf der Django-App-Seite enthalten. Ich möchte es einfangen.
In Verbindung stehender Artikel: Django + Shibboleth mit RemoteUserMiddleware (Erläuterung der Einführung von RemoteUserMiddleware)
--Windows Server 2016 (nicht direkt verwandt)
RemoteUserBackend
durchsucht das durch settings.AUTH_USER_MODEL
angegebene Benutzermodell nach einem Objekt, dessen Benutzername die Umgebungsvariable REMOTE_USER enthält. Wenn gefunden, melden Sie sich als dieser Benutzer an. Wenn nicht gefunden, erstellen Sie ein Benutzerobjekt mit REMOTE_USER als Benutzername und melden Sie sich an (diese Einstellung kann geändert werden).
configure_user
Ursprünglich verwendet "RemoteUserBackend" die Methode zum Bearbeiten des Benutzerobjekts beim Erstellen des Benutzerobjekts auf der Seite der Django-App.
python:django.contrib.auth.backends
class RemoteUserBackend(ModelBackend):
...
def configure_user(self, request, user):
"""
Configure a user after creation and return the updated user.
By default, return the user unmodified.
"""
return user
Erstellen Sie "backends.py" (einen beliebigen Namen) in einer geeigneten App.
appname
└── appname
├── templates
├── __init__.py
├── settings.py
├── urls.py
├── views.py
├── wsgi.py
└── backends.py #← Erstellen
Überschreiben Sie configure_user
. Dieses Mal habe ich beschlossen, das von Shibboleth hinzugefügte Attribut "ATR_mail`" in den HTTP-Header im User-Objekt einzugeben.
backends.py
from django.contrib.auth.backends import RemoteUserBackend
class MyRemoteUserBackend(RemoteUserBackend):
def configure_user(self, request, user: MyUser):
user.email = request.META['ATR_mail']
user.save() #← Vergiss nicht! !!
return user
Um dies zu aktivieren, müssen Sie AUTHENTICATION_BACKENDS
in settings.py
ändern.
settings.py
AUTHENTICATION_BACKENDS = (
# 'django.contrib.auth.backends.RemoteUserBackend', #← Löschen
'appname.backends.MyRemoteUserBackend', #← Hinzufügen
'django.contrib.auth.backends.ModelBackend',
)
Durch einfaches Überschreiben von "configure_user" wird "configure_user" nur ausgeführt, wenn Sie ein neues Benutzerobjekt für REMOTE_USER erstellen, das für Django neu ist, und sich anmelden. ** ** **
Das vorhandene Benutzerobjekt wird nicht aktualisiert, selbst wenn sich die Attributinformationen auf der IdP-Seite ändern. Dies scheint jedoch in vielen Fällen unpraktisch zu sein. Wenn Sie beispielsweise SSO in einem Unternehmen verwenden und sich die Abteilungs- oder Nebenstellennummer von Zeit zu Zeit ändert.
In diesem Fall müssen Sie "authentifizieren" ändern. Glücklicherweise können Sie das zuvor erstellte "MyRemoteUserBackend" von "backends.py" verwenden. Die meisten sind Kopien von "authentifizieren" von "RemoteUserBackend" der Superklasse.
backends.py
from django.contrib.auth.backends import RemoteUserBackend
from django.contrib.auth.models import User
import inspect
import warnings
from django.contrib.auth import get_user_model
from django.utils.deprecation import RemovedInDjango31Warning
UserModel = get_user_model()
class MyRemoteUserBackend(RemoteUserBackend):
def authenticate(self, request, remote_user):
"""
The username passed as ``remote_user`` is considered trusted. Return
the ``User`` object with the given username. Create a new ``User``
object if ``create_unknown_user`` is ``True``.
Return None if ``create_unknown_user`` is ``False`` and a ``User``
object with the given username is not found in the database.
"""
if not remote_user:
return
user = None
username = self.clean_username(remote_user)
# Note that this could be accomplished in one try-except clause, but
# instead we use get_or_create when creating unknown users since it has
# built-in safeguards for multiple threads.
if self.create_unknown_user:
user, created = UserModel._default_manager.get_or_create(**{
UserModel.USERNAME_FIELD: username
})
if created: #← Achtung
args = (request, user)
try:
inspect.getcallargs(self.configure_user, request, user)
except TypeError:
args = (user,)
warnings.warn(
'Update %s.configure_user() to accept `request` as '
'the first argument.'
% self.__class__.__name__, RemovedInDjango31Warning
)
user = self.configure_user(*args) #← Achtung
else: #← Hinzufügen
user = self.configure_user(request, user) #← Hinzufügen
else:
try:
user = UserModel._default_manager.get_by_natural_key(username)
except UserModel.DoesNotExist:
pass
return user if self.user_can_authenticate(user) else None
def configure_user(self, request, user: User):
user.email = request.META['ATR_mail']
user.save()
return user
Wie in "Achtung" angegeben, wird "configure_user" ursprünglich nur aufgerufen, wenn "created == True" in "UserModel._default_manager.get_or_create". Auf diese Weise können Sie vorhandene Benutzerinformationen nicht aktualisieren.
Fügen Sie daher zwei Zeilen "add" hinzu, damit "configure_user" auch dann aufgerufen wird, wenn "created! = True". Wenn Sie den Vorgang zum Erstellen und Aktualisieren eines neuen Benutzerobjekts trennen möchten, können Sie "configure_user" imitieren und einen neuen "update_user" usw. erstellen, damit es aufgerufen wird.
das ist alles.
Recommended Posts