2012-05-22 9 views
24

Sto tentando di utilizzare Flask e l'estensione Flask-Login per implementare l'autenticazione utente in un'app Flask. L'obiettivo è quello di estrarre le informazioni sull'account utente da un database e quindi accedere a un utente, ma mi sto bloccando; tuttavia, l'ho ristretto a una particolare parte del comportamento di Flask-Login.Come implementare la callback user_loader in Flask-Login

In base allo Flask-Login documentation, ho bisogno di creare una funzione "callback" user_loader. Lo scopo effettivo e l'implementazione di questa funzione mi hanno confuso per alcuni giorni:

Sarà necessario fornire un callback user_loader. Questo callback viene utilizzato per ricaricare l'oggetto utente dall'ID utente archiviato nella sessione. Lo dovrebbe prendere l'ID Unicode di un utente e restituire l'oggetto utente corrispondente. Per esempio:

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

Ora, dicono voglio all'utente di inserire un nome e una password in una forma, controllare un database, e accedere l'utente. La roba del database funziona bene e non è un problema per me.

Questa funzione di "callback" deve essere passata a un ID utente # e restituire l'oggetto Utente (il cui contenuto sto caricando da un database). Ma in realtà non capisco cosa debba fare il check-in, dal momento che gli ID utente vengono comunque prelevati dallo stesso posto. Posso "ordinare" il callback per funzionare, ma sembra disordinato/hackish e colpisce il database con ogni singola risorsa richiesta dal browser. Veramente non voglio controllare il mio database per scaricare favicon.ico con ogni aggiornamento di pagina, ma il login di flask sembra che lo stia forzando.

Se non controllo nuovamente il database, non è possibile restituire un oggetto Utente da questa funzione. L'oggetto/classe Utente viene creato nel percorso del pallone per l'accesso e non rientra quindi nell'ambito del callback.

Quello che non riesco a capire è come passare un oggetto Utente in questa funzione di callback, senza dover colpire il database ogni volta. Oppure, altrimenti, capire come fare per farlo in un modo più efficace. Devo mancare qualcosa di fondamentale, ma l'ho guardato per alcuni giorni, gettando tutti i tipi di funzioni e metodi, e niente sta funzionando.

Ecco alcuni snippet pertinenti dal mio codice di test. La classe utente:

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 funzione che ho fatto per restituire l'oggetto utente alla funzione di Flask Login user_loader callback:

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 

Il 'richiamata', che non del tutto a capire (deve restituire l'oggetto d'uso, che viene creato dopo aver estratto dal database):

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

il percorso login:

@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") 

Il mio codice 'kinda' funziona, posso accedere e uscire, ma come ho detto, deve colpire il database per assolutamente tutto, perché devo fornire un oggetto User alla funzione di callback in un namespace/scope differente da dove ha luogo il resto dell'azione di accesso. Sono abbastanza sicuro che sto sbagliando tutto, ma non riesco a capire come.

Il codice di esempio fornito da flask-login does it this way, ma questo funziona solo perché sta estraendo gli oggetti User da un dizionario hard-coded globale, non come in uno scenario reale come un database, in cui il DB deve essere controllato e gli oggetti utente creati dopo l'utente inserisce le proprie credenziali di accesso. E non riesco a trovare nessun altro codice di esempio che illustri l'utilizzo di un database con login-flask.

Cosa manca qui?

+0

che strano. Il tuo login() non controlla nemmeno la password. – Houman

+2

@hooman Questo era solo a scopo di esempio. –

risposta

16

È necessario caricare l'oggetto utente dal DB ad ogni richiesta. Il motivo più forte per questo requisito è che Flask-Login controllerà il token di autenticazione ogni volta per garantirne la validità continua. Il calcolo di questo token potrebbe richiedere parametri memorizzati sull'oggetto utente.

Ad esempio, si supponga che un utente abbia due sessioni simultanee. In uno di questi, l'utente cambia la propria password. Nelle richieste successive, l'utente deve essere disconnesso dalla seconda sessione e costretto a riconnettersi nuovamente affinché l'applicazione sia sicura. Pensa al caso in cui la seconda sessione viene rubata perché l'utente ha dimenticato di disconnettersi da un computer: si desidera una modifica della password per risolvere immediatamente la situazione. Potresti anche voler dare ai tuoi amministratori la possibilità di cacciare un utente.

Affinché tale disconnessione forzata avvenga, il token di autenticazione memorizzato in un cookie deve 1) essere basato in parte sulla password o qualcos'altro che cambia ogni volta che viene impostata una nuova password; 2) essere controllato prima di eseguire qualsiasi vista, rispetto agli ultimi attributi noti dell'oggetto utente, che sono memorizzati nel DB.

+1

Grazie per questa spiegazione. Quindi non è necessariamente che il mio approccio/soluzione finora fosse sbagliato, ma piuttosto che ho fatto alcune ipotesi sbagliate sul perché stava funzionando in quel modo. –

Problemi correlati