J'espère que vous pourrez m'aider à trouver la meilleure façon d'implémenter une connexion manuelle (initiée côté serveur) sans en utilisant le mot de passe. Permettez-moi d'expliquer le flux de travail:
Ce que j'essaie de faire, c'est de me connecter à l'utilisateur après avoir cliqué sur le lien de messagerie afin qu'il puisse commencer à utiliser le site Web immédiatement.
Je ne peux pas utiliser son mot de passe car il est crypté dans la base de données, la seule option est-elle d'écrire un backend d'authentification personnalisé?
Vous n'avez pas besoin d'un mot de passe pour vous connecter. Le auth.login
function prend simplement un objet User
, que vous obtenez probablement déjà de la base de données lorsque vous activez le compte. Vous pouvez donc le transmettre directement à login
.
Bien sûr, vous devrez faire très attention à ce qu'aucun utilisateur ne puisse usurper un lien vers un compte existant déjà activé, qui serait alors connectez-les automatiquement en tant qu'utilisateur.
from Django.contrib.auth import login
def activate_account(request, hash):
account = get_account_from_hash(hash)
if not account.is_active:
account.activate()
account.save()
user = account.user
login(request, user)
... etc.
Modifié:
Hmm, je n'ai pas remarqué cette exigence d'utiliser authenticate
en raison de la propriété supplémentaire qu'il ajoute. En regardant le code, tout ce qu'il fait est un attribut backend
équivalent au chemin du module du backend d'authentification. Donc, vous pouvez simplement le simuler - avant l'appel de connexion ci-dessus, procédez comme suit:
user.backend = 'Django.contrib.auth.backends.ModelBackend'
Depuis Django 1.10, le processus a été simplifié.
Dans toutes les versions de Django, pour qu'un utilisateur soit connecté, il doit être authentifié par l'un des backends de votre application (contrôlé par le AUTHENTICATION_BACKENDS
réglage).
Si vous souhaitez simplement forcer une connexion, vous pouvez simplement affirmer que l'utilisateur a été authentifié par le premier backend de cette liste:
from Django.conf import settings
from Django.contrib.auth import login
# Django 1.10+
login(request, user, backend=settings.AUTHENTICATION_BACKENDS[0])
# Django <1.10 - fake the authenticate() call
user.backend = settings.AUTHENTICATION_BACKENDS[0]
login(request, user)
La réponse de Daniel est très bonne.
Une autre façon de le faire est de créer un HashModelBackend en suivant les backends d'autorisation personnalisée https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#writing-an-authentication-backend comme ça:
class HashModelBackend(object):
def authenticate(self, hash=None):
user = get_user_from_hash(hash)
return user
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Et puis installez ceci dans vos paramètres:
AUTHENTICATION_BACKENDS = (
'myproject.backends.HashModelBackend',
'Django.contrib.auth.backends.ModelBackend',
)
Ensuite, votre vue serait quelque chose comme ceci:
def activate_account(request, hash):
user = authenticate(hash=hash)
if user:
# check if user is_active, and any other checks
login(request, user)
else:
return user_not_found_bad_hash_message
Vous pouvez utiliser le package ska
, qui a une connexion sans mot de passe à Django implémenté. ska
fonctionne avec des jetons d'authentification et sa sécurité est basée sur SHARED_KEY qui devrait être égal pour toutes les parties (serveurs) impliquées.
Côté client (partie qui demande une connexion sans mot de passe), vous générez une URL et la signez, à l'aide de ska
. Exemple:
from ska import sign_url
from ska.contrib.Django.ska.settings import SECRET_KEY
server_ska_login_url = 'https://server-url.com/ska/login/'
signed_url = sign_url(
auth_user='test_ska_user_0',
secret_key=SECRET_KEY,
url=server_ska_login_url
extra={
'email': '[email protected]',
'first_name': 'John',
'last_name': 'Doe',
}
)
La durée de vie par défaut du jeton est de 600 secondes. Vous pouvez personnaliser cela en prouvant un argument lifetime
.
Côté serveur (site auquel les utilisateurs se connectent), sachant que vous avez correctement installé ska
, l'utilisateur est connecté en visitant l'URL si elle existait (correspondance de nom d'utilisateur), ou autrement - créée . Il existe 3 rappels que vous pouvez personnaliser dans les paramètres Django de Django).
USER_GET_CALLBACK
(chaîne): déclenché si l'utilisateur a été récupéré avec succès à partir de la base de données (utilisateur existant).USER_CREATE_CALLBACK
(chaîne): tiré juste après la création de l'utilisateur (l'utilisateur n'existait pas).USER_INFO_CALLBACK
(chaîne): déclenché en cas d'authentification réussie.Voir la documentation ( http://pythonhosted.org/ska/ ) pour plus.
Réponse à la réponse de dan .
Une façon d'écrire votre backend:
from Django.contrib.auth import get_user_model
from Django.contrib.auth.backends import ModelBackend
class HashModelBackend(ModelBackend):
def authenticate(self, username=None, **kwargs):
UserModel = get_user_model()
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
user = UserModel._default_manager.get_by_natural_key(username)
return user
except UserModel.DoesNotExist:
return None
La réponse est basée sur le code source de Django.contrib.auth.backends.ModelBackend . C'est réel pour Django 1.9
Et je préfère placer un backend personnalisé en dessous de la valeur par défaut de Django:
AUTHENTICATION_BACKENDS = [
'Django.contrib.auth.backends.ModelBackend',
'yours.HashModelBackend',
]
car l'activation du compte est moins possible que la connexion elle-même. Selon https://docs.djangoproject.com/en/1.9/topics/auth/customizing/#specifying-authentication-backends :
L'ordre des AUTHENTICATION_BACKENDS est important, donc si le même nom d'utilisateur et le même mot de passe sont valides dans plusieurs backends, Django arrêtera le traitement lors de la première correspondance positive.
Attention ce code authentifiera vos utilisateurs même avec des mots de passe incorrects.