Manejo de sesiones con Flask-Login
En este artículo veremos como manejar sesiones en una aplicación Flask utilizando la extensión Flask-Login. También exploraremos los pasos necesarios para proteger rutas en nuestra aplicación.
Flask-Login es una extensión de Flask que permite el manejo de sesiones. Se adapta a diferentes tipos de autenticación y no está restringido al uso de usuario-contraseña haciéndolo lo suficientemente flexible para adaptarse a distintos tipos de proyectos incluyendo aquellos que utilicen métodos de autenticación externos.
Flask-Login cuenta con las siguientes características:
- Manejo de Login: La ID del usuario es almacenada en la sesión permitiendo la identificación del usuario para la restricción de acceso a rutas
- Manejo de Logout
- Restricción de rutas: Permite la restricción de rutas dependiendo de la identidad del usuario y si tiene una sesión iniciada
- Almacenamiento no predeterminado: Es posible configurar cómo se obtiene la identidad del usuario (Base de datos, Token, etc)
- Método de autenticación no predeterminado: Es posible configurar cualquier tipo de autenticación (usuario-contraseña, JWT, OpenID, etc)
Instalación de Flask-Login
Es posible instalar Flask-Login utilizando pip
pip3 install flask-login
Configuración
Para utilizar Flask-Login es necesario inicializar la clase LoginManager
que será encargada de
manejar la funciones de sesión. Al inicializarla se pasa como argumento la variable que contiene la aplicación de Flask.
app = Flask(__name_)
login_manager = LoginManager(app)
Para que el manejo de sesiones funcione correctamente es necesario que la aplicación tenga cargada
la clave secreta.
app.secret_key = “clave_secreta”
Clase de usuario
Para poder manejar la sesión de usuario debe crearse una clase que representará al usuario. Flask-Login requiere que la clase de usuario implemente los siguientes métodos y propiedades.
is_authenticated | Esta propiedad debe devolver True si el usuario está autenticado y False de lo contrario |
is_active | Esta propiedad debe devolver True si el usuario está activo. Esto dependerá de las condiciones programadas en la aplicación (activación de cuenta, suspensión de cuenta, etc) |
is_anonymous | Esta propiedad debe devolver True si el usuario en anónimo. Los usuarios reales siempre devolverán False . |
get_id() | Este método debe devolver un valor unicode que identifique al usuario. |
En vez de implementar manualmente estos métodos es posible hacer que la clase de usuario herede de la clase UserMixin
que tiene estos métodos definidos.
class Usuario(UserMixin):
Carga de usuario
Para indicar a LoginManager cómo obtener el usuario a partir del ID guardado en la sesión debe crearse la función load_user
. Existen dos decoradores que se utilizarán dependiendo del método de autenticación.
Se utiliza el decorador user_loader
para recargar el usuario desde la sesión. La función tomará el ID
de guardado en la sesión y devolverá el objeto Usuario
o None
si el usuario no existe. En el siguiente ejemplo se busca el usuario en la base de datos a partir del ID de la sesión.
@login_manager.user_loader
def load_user(usuario_id):
#Buscar en BD y cargar atributos
return Usuario.get(usuario_id)
Si en cambio los datos del usuario utilizando una request de Flask se utiliza el decorador request_loader
. La función debe tomar como entrada una request
y devolverá el objeto Usuario
o None
si el usuario no existe. En el siguiente caso se obtienen los datos del usuario de un JWT.
@login_manager.request_loader
def load_user(request):
if 'access_token' in request.cookies:
#Decodificar el token
decoded = jwt.decode(request.cookies['access_token'], current_app.config["SECRET_KEY"])
#Cargar datos del usuario
user = Usuario(decoded["email"],decoded["nombre"])
#Devolver usuario
return user
return None
Inicio de sesión
Para realizar el login de un usuario se debe pasar a la función login_user
el objeto de la clase de usuario que se quiere cargar y como segundo parámetro si se quiere que se recuerde la sesión. Previo a este paso se debe haber realizado el proceso de autenticación según el método elegido (verificar usuario y contraseña, verificar token, obtener confirmación de método de autenticación externo, etc)
usuario = Usuario(id =5, nombre ="Pedro" )
login_user(usuario)
Una vez realizado este proceso la sesión se iniciará con el ID
del usuario cargado junto con todos los datos de la clase.
Cierre de sesión
Para cerrar la sesión abierta se utiliza la función logout_user
.
logout_user()
Obtener datos de sesión
Para obtener los datos de una sesión o verificar si la sesión ha sido creada se puede utilizar el proxy current_user
. Dentro de un template es posible mostrar los atributos correspondientes al usuario que ha iniciado sesión o acceder a sus métodos.
Hola{{ current_user.nombre }}!
Ya que la clase usuario hereda de UserMixin
es posible acceder a los métodos para verificar que el usuario no sea anónimo y restringir el código renderizado si no lo está. De esta manera es posible mostrar solo aquellos enlaces, botones o contenido requerido para cada tipo de usuario.
{% if current_user.is_authenticated %}
Hola{{ current_user.nombre }}!
{% endif %}
Cuando ningún usuario ha iniciado sesión current_user
contiene un objeto de tipoAnonymousUserMixin
. Este objeto tiene implementados los mismos métodos y propiedades que la
clase UserMixin
por lo que el método is_authenticated
está implementado y tendrá un valor de False
.
Un caso típico es mostrar el enlace de login o formulario de login a usuarios no logueados y el enlace de logout a los que han iniciado sesión.
{% if current_user.is_authenticated %}
<a href="{{ url_for('main.logout') }}">Logout</a>
{% else %}
<a href="{{ url_for('main.login') }}">Login</a>
{% endif %}
Restricción de rutas
Para restringir el acceso a las rutas que requieren que el usuario haya iniciado sesión para accederlas se utiliza el decorador login_required
sobre la ruta.
@app.route('/usuario')
@login_required
def usuario():
return render_template('usuario.html')
Cuando un usuario intenta ingresar a una vista que tiene el decorador login_required
sin haber
iniciado sesión por defecto Flask mostrará un mensaje de error y abortará con un error 401. Para
personalizar este comportamiento puede utilizarse el decorador unauthorized_handler
.
El el siguiente ejemplo el usuario será redireccionado a la página que contiene el formulario de login y un mensaje de advertencia se mostrará en su pantalla.
@login_manager.unauthorized_handler
def unauthorized_callback():
flash('Debe iniciar sesión para continuar.')
return redirect(url_for('login'))
En este artículo hemos visto como crear, cerrar y acceder a datos de sesiones en Flask utilizando la extensión Flask-Login. Espero que les sea de utilidad!