2010-05-07 20 views
54

Espero que pueda ayudarme a encontrar la mejor forma de implementar un inicio de sesión manual (iniciado en el servidor) sin usando la contraseña. Voy a explicar el flujo de trabajo: RegistrosIniciar sesión manualmente en un usuario sin contraseña

  • usuario
  • Gracias! Un correo electrónico con un enlace de activación ha sido enviado blablabla
  • (Cuenta ahora existe pero no está marcado activado)
  • usuario abre el correo electrónico, hace clic en enlace
  • (cuenta está habilitada)
  • Gracias! Ahora puede usar el sitio

Lo que trato de hacer es iniciar sesión en el usuario después de haber hecho clic en el enlace del correo electrónico para que pueda comenzar a utilizar el sitio web de inmediato.

No puedo usar su contraseña porque está encriptada en la base de datos, ¿es la única opción que escribe un back-end de autenticación personalizado?

Respuesta

78

No necesita una contraseña para registrar a un usuario. El auth.login function simplemente toma un objeto User, que presumiblemente ya está obteniendo de la base de datos cuando habilita la cuenta. Entonces puede pasar eso directamente al login.

Por supuesto, tendrá que ser muy teniendo cuidado de que no haya manera de que un usuario pueda falsificar un enlace a una cuenta existente ya habilitada, que luego las iniciará automáticamente como ese usuario.

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

Editado:

Hmm, no se dio cuenta de que la prescripción de utilizar authenticate debido a la propiedad adicional que se agrega. Al mirar el código, todo lo que hace es un atributo backend equivalente a la ruta del módulo del servidor de autenticación. Entonces puede simplemente falsificarlo - antes de la llamada de inicio de sesión anterior, haga esto:

user.backend = 'django.contrib.auth.backends.ModelBackend' 
+1

Gracias; los documentos están de acuerdo, pero también está esta advertencia: "Realizando una llamada a authenticate() primero Cuando ingresa manualmente a un usuario, debe llamar a authenticate() antes de llamar a login().authenticate() establece un atributo en el usuario que indica qué servidor de autenticación autenticó correctamente a ese usuario (consulte la documentación de los back-end para obtener más información) y esta información se necesita más adelante durante el proceso de inicio de sesión. " ¿Podría ser un problema? – Agos

+0

Ver mi actualización arriba . –

+0

Aunque es mejor importar configuraciones de django.contrib.conf y asignar configuraciones.AUTHENTICATION_BACKENDS en caso de que el uso tenga un back-end personalizado. – Arsham

25

La respuesta de Daniel es muy buena.

Otra manera de hacerlo es crear un HashModelBackend siguiendo los backends de autorización personalizado https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#writing-an-authentication-backend como esto:

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 

e instalarlo en su configuración:

AUTHENTICATION_BACKENDS = (
    'myproject.backends.HashModelBackend', 
    'django.contrib.auth.backends.ModelBackend', 
) 

Luego, su punto de vista sería algo como esto:

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 
2

Puede usar ska paquete, que tiene inicio de sesión sin contraseña a Django implementado. ska funciona con tokens de autenticación y su seguridad se basa en SHARED_KEY, que debe ser igual para todas las partes involucradas (servidores).

En el lado del cliente (parte que solicita un inicio de sesión sin contraseña), genera una URL y la firma, usando ska. Ejemplo:

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 vida útil predeterminada del token es 600 segundos. Puede personalizar eso probando un argumento lifetime.

En el lado del servidor (sitio al que inician sesión los usuarios), teniendo en cuenta que ha instalado ska correctamente, el usuario ha iniciado sesión al visitar la URL si existieron (coincidencia de nombre de usuario) o creada de otro modo . Hay 3 devoluciones de llamada que puede personalizar en la configuración de Django de su proyecto.

USER_GET_CALLBACK (cadena): se desencadena si el usuario se recuperó correctamente de la base de datos (usuario existente). USER_CREATE_CALLBACK (cadena): se desencadena justo después de que se haya creado el usuario (el usuario no existía). USER_INFO_CALLBACK (cadena): se activa tras una autenticación exitosa.

Consulte la documentación (http://pythonhosted.org/ska/) para más información.

2

Respuesta a dan la respuesta.

Una manera de escribir su base de:

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 

respuesta se basa en django.contrib.auth.backends.ModelBackend código fuente. Es real para Django 1.9

Y yo preferiría lugar backend personalizado debajo de omisión de Django:

AUTHENTICATION_BACKENDS = [ 
    'django.contrib.auth.backends.ModelBackend', 
    'yours.HashModelBackend', 
] 

debido a la activación de la cuenta es menos posible despues entrar en sí. De acuerdo con https://docs.djangoproject.com/en/1.9/topics/auth/customizing/#specifying-authentication-backends:

El orden de las cosas AUTHENTICATION_BACKENDS, por lo que si el mismo nombre de usuario y la contraseña es válida en varios respaldos, Django procesamiento se detenga tras la primera coincidencia positiva.

Tenga cuidado este código va a autenticar a los usuarios incluso con contraseñas incorrectas.

+0

Existen errores de sintaxis en este código, por lo que no está claro lo que se supone que debe suceder ('hash' está definido pero no utilizado, se usa 'username' pero no está definido). –

+0

@ amichai-schreiber gracias! He corregido la respuesta –

14

A partir de Django 1.10, el proceso se ha simplificado. Para todas las versiones de Django, para que un usuario inicie sesión, debe ser authenticated by one of your app's backends (controlado por AUTHENTICATION_BACKENDS).

Si simplemente quiere forzar un inicio de sesión, sólo se puede afirmar que el usuario se ha autenticado por el primer back-end de esa lista:

from django.conf import settings 
from django.contrib.auth import login 


# Django 1.10+ 
login(request, user, backend=settings.settings.AUTHENTICATION_BACKENDS[0]) 

# Django <1.10 - fake the authenticate() call 
user.backend = settings.AUTHENTICATION_BACKENDS[0] 
login(request, user) 
Cuestiones relacionadas