2012-05-22 12 views
24

Estoy intentando usar Flask y la extensión Flask-Login para implementar la autenticación de usuario en una aplicación Flask. El objetivo es extraer la información de la cuenta de usuario de una base de datos y luego iniciar sesión en un usuario, pero me estoy atascado; sin embargo, lo he reducido a una parte en particular del comportamiento de inicio de sesión de Flask.Cómo implementar la devolución de llamada user_loader en Flask-Login

De acuerdo con Flask-Login documentation, necesito crear una función de "devolución de llamada" user_loader. El propósito real y la implementación de esta función me han confundido por unos días:

Deberá proporcionar una devolución de llamada user_loader. Esta devolución de llamada se usa para volver a cargar el objeto de usuario desde la ID de usuario almacenada en la sesión. Es debe tomar el identificador Unicode de un usuario y devolver el correspondiente objeto de usuario . Por ejemplo:

@login_manager.user_loader 
def load_user(userid): 
    return User.get(userid) 

Ahora, digo que quiero que el usuario introduzca un nombre y una contraseña en un formulario, compruebe contra una base de datos, e ingrese el usuario. La información de la base de datos funciona bien y no es un problema para mí.

Esta función de "devolución de llamada" quiere que se pase un ID de usuario #, y devuelva el objeto Usuario (cuyo contenido estoy cargando desde una base de datos). Pero realmente no obtengo lo que se supone que debe verificar/hacer, ya que las identificaciones de usuario son todas extraídas del mismo lugar de todos modos. Puedo 'ordenar' que la devolución de llamada funcione, pero parece desordenada/hackosa y llega a la base de datos con cada recurso que solicita el navegador. Realmente no quiero verificar mi base de datos para descargar favicon.ico con cada actualización de página, pero el matraz de inicio de sesión parece estar forzando esto.

Si no vuelvo a consultar la base de datos, entonces no tengo forma de devolver un objeto Usuario desde esta función. El objeto/clase del usuario se crea en la ruta del matraz para iniciar sesión y, por lo tanto, está fuera del alcance de la devolución de llamada.

Lo que no puedo entender es cómo pasar un objeto Usuario a esta función de devolución de llamada, sin tener que pulsar la base de datos cada vez. O, de lo contrario, averigüe cómo hacer esto de una manera más efectiva. Debo estar perdiendo algo fundamental, pero lo he estado mirando durante unos días, lanzando todo tipo de funciones y métodos, y nada está funcionando.

Aquí están los fragmentos relevantes de mi código de prueba. La clase de usuario:

class UserClass(UserMixin): 
    def __init__(self, name, id, active=True): 
      self.name = name 
      self.id = id 
      self.active = active 

    def is_active(self): 
      return self.active 

La función que hice para devolver el objeto de usuario a la función del frasco de sesión user_loader devolución de llamada:

def check_db(userid): 

    # query database (again), just so we can pass an object to the callback 
    db_check = users_collection.find_one({ 'userid' : userid }) 
    UserObject = UserClass(db_check['username'], userid, active=True) 
    if userObject.id == userid: 
      return UserObject 
    else: 
      return None 

la 'devolución de llamada', que no entiendo por completo (debe volver el objeto de usuario, que se crea después de tirar de la base de datos):

@login_manager.user_loader 
def load_user(id): 
    return check_db(id) 

la ruta de acceso:

@app.route("/login", methods=["GET", "POST"]) 
def login(): 
    if request.method == "POST" and "username" in request.form: 
      username = request.form["username"] 

      # check MongoDB for the existence of the entered username 
      db_result = users_collection.find_one({ 'username' : username }) 

      result_id = int(db_result['userid']) 

      # create User object/instance 
      User = UserClass(db_result['username'], result_id, active=True) 

      # if username entered matches database, log user in 
      if username == db_result['username']: 
       # log user in, 
       login_user(User) 
       return url_for("index")) 
      else: 
       flash("Invalid username.") 
     else: 
      flash(u"Invalid login.") 
     return render_template("login.html") 

Mi código 'kinda' funciona, puedo iniciar y cerrar sesión, pero como dije, debe golpear la base de datos para absolutamente todo, porque tengo que proporcionar un objeto Usuario a la función de devolución de llamada en un espacio de nombre/alcance diferente desde donde tiene lugar el resto de la acción de inicio de sesión. Estoy bastante seguro de que estoy haciendo todo mal, pero no puedo entender cómo.

El código de ejemplo proporcionado por el matraz does it this way, pero esto solo funciona porque está extrayendo los objetos del usuario de un diccionario global codificado, no como en un escenario del mundo real como una base de datos, donde se debe verificar el DB y objetos de usuario creados después de el usuario ingresa sus credenciales de inicio de sesión. Y parece que no puedo encontrar ningún otro código de ejemplo que ilustre el uso de una base de datos con el nombre de usuario frasco.

¿Qué aquí se encuentra falta?

+0

qué extraño. Su inicio de sesión() ni siquiera comprueba si hay alguna contraseña. – Houman

+2

@hooman Esto fue solo para propósitos de ejemplo. –

Respuesta

16

Deberá cargar el objeto de usuario del DB en cada solicitud. La razón más fuerte para este requisito es que Flask-Login verificará el token de autenticación cada vez para garantizar su validez continua. El cálculo de este token puede requerir parámetros almacenados en el objeto del usuario.

Por ejemplo, supongamos que un usuario tiene dos sesiones simultáneas. En uno de ellos, el usuario cambia su contraseña. En las solicitudes posteriores, el usuario debe cerrar la sesión de la segunda sesión y debe volver a iniciar sesión para que su aplicación sea segura. Piense en el caso en el que se roba la segunda sesión porque su usuario olvidó cerrar la sesión de una computadora: desea cambiar la contraseña para solucionar la situación de inmediato. También es posible que desee dar a sus administradores la capacidad de expulsar a un usuario.

Para que se produzca el cierre de sesión forzado, el token de autenticación almacenado en una cookie debe 1) basarse en parte en la contraseña u otra cosa que cambie cada vez que se establezca una nueva contraseña; 2) se verifica antes de ejecutar cualquier vista, en contra de los últimos atributos conocidos del objeto de usuario, que se almacenan en el DB.

+1

Gracias por esa explicación. Por lo tanto, no es necesariamente que mi enfoque/solución haya estado equivocado hasta ahora, sino que hice algunas suposiciones incorrectas sobre por qué estaba funcionando de esa manera. –

Cuestiones relacionadas