Gestión de usuarios con sesiones
En esta unidad vamos a introducir el concepto de sesión para posibilitar que los usuarios de nuestra página puedan loguearse en ella. Posteriormente veremos como autorizar el acceso a las distintas opciones de nuestra aplicación según el rol del usuario. En esta unidad vamos a trabajar directamente con sesiones, en una unidad posterior utilizarnos la extensión de Flask flask-login
para realizar la autentificación.
El modelo de datos para guardar los usuarios
Necesitamos una nueva tabla en nuestra base de datos para guardar los usuarios, para ello en nuestro modelo de datos (fichero models.py
) añadimos la clase Usuarios
:
class Usuarios(db.Model):
"""Usuarios"""
__tablename__ = 'usuarios'
id = Column(Integer, primary_key=True)
username = Column(String(100),nullable=False)
password_hash = Column(String(128),nullable=False)
nombre = Column(String(200),nullable=False)
email = Column(String(200),nullable=False)
admin = Column(Boolean, default=False)
def __repr__(self):
return (u'<{self.__class__.__name__}: {self.id}>'.format(self=self))
@property
def password(self):
raise AttributeError('password is not a readable attribute')
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
return check_password_hash(self.password_hash, password)
Algunas indicaciones interesantes:
- Los datos de usuario se van a guardar en una tabla llamada
usuarios
. - Guardamos un identificador, un nombre de usuario, contraseña (que estará cifrada), el nombre, el correo electrónico y un valor lógico (admin) que nos indica si el usuario es administrador. Vamos a tener dos roles de usuarios; administradores y usuarios normales.
- Tenemos una propiedad
password
. Al intentar obtener su valor, nos devuelve una excepción indicado que no se puede leer, si intentamos modificarla, lo que realmente se hace es cifrarla en el atributopassword_hash
con la funcióngenerate_password_hash
del módulowerkzeug.security
. - También tenemos un método
verify_password
que utilizando la funcióncheck_password_hash
del módulowerkzeug.security
, nos permite verificar si la contraseña guarda es igual a la indicada como parámetro.
Tenemos que volver a generar las tablas para tener a nuestra disposición el nuevo modelo. Una vez realizada esta operación podemos hacer una prueba, creando un usuario:
>>> from aplicacion.app import db
>>> from aplicacion.models import Usuarios
>>> u=Usuarios()
>>> u.nombre="pepe"
>>> u.password="asdasd"
>>> u.username="pepe"
>>> u.email="a@a.es"
>>> db.session.add(u)
>>> db.session.commit()
>>> u.password_hash
>>> 'pbkdf2:sha256:50000$EFhxMbr1$ea8e6ddeaaac8d73d01f78f1b3d3120184cc25aea9491e632b4fc8c9ae2705cb'
>>> u.password
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/jose/github/curso_flask/ejemplos/u27/aplicacion/models.py", line 53, in password
raise AttributeError('password is not a readable attribute')
AttributeError: password is not a readable attribute
Creación del usuario administrador
Para facilitar la creación de un primer usuario con rol administrador hemos introducido una nueva funcionalidad en nuestro script manage.py
:
@manager.command
def create_admin():
usuario={"username":input("Usuario:"),
"password":getpass("Password:"),
"nombre":input("Nombre completo:"),
"email":input("Email:"),
"admin": True}
usu=Usuarios(**usuario)
db.session.add(usu)
db.session.commit()
Que nos pide los datos del usuario por teclado y crea un usuario administrador:
$ python3 manage.py create_admin
Usuario:
...
Autentficando usuarios en nuestra aplicación
Una vez que tenemos preparado nuestro modelo de datos y utilizando sesiones vamos a programar la posibilidad de que un usuario se autentifique en nuestra aplicación y simulemos una sesión en ella hasta que salga del sistema. Para ello vamos a realizar los siguientes pasos:
-
El formulario
LoginForm
nos va posibilitar pedir nombre de usuario y contraseña para verificar si es un usuario correcto, por lo tanto en el ficheroforms.py
:class LoginForm(FlaskForm): username = StringField('Login', validators=[Required()]) password = PasswordField('Password', validators=[Required()]) submit = SubmitField('Entrar')
-
Flask nos permite trabajar con sesiones, hemos creado un fichero
login.py
con el siguiente contenido:def login_user(Usuario): session["id"]=Usuario.id session["username"]=Usuario.username session["admin"]=Usuario.admin def logout_user(): session.pop("id",None) session.pop("username",None) session.pop("admin",None)
Cuando un usuario se haya logueado de manera adecuada, utilizaremos la función
login_user
para crear variables de sesiones con la información del identificador, el nombre de usuario y su rol. si el usuario sale del sistema se utilizará la funciónlogout_user
para borrar dichas variables y terminar la sesión. -
Por lo tanto si existe alguna de las variables
session
tendremos un usuario logueado en el sistema. Esta variable es accesible desde las plantillas, por lo tanto en la plantillabase.html
podemos introducir el siguiente código:<a class="navbar-brand " href="/login">Login</a>
Si existe la variable
session["id"]
tenemos un usuario en el sistema: ponemos su nombre de usuario y un enlace a “Salir”. Si esa variable no existe ponemos un enlace para posibilitar que el usuario introduzca sus credenciales. -
En el programa principal, creamos una ruta
login
que muestra el formulario de login, si mandamos el formulario con éxito busca el usuario en la base de datos y comprueba la contraseña indicada si todo es correcto crea la sesión con la funciónlogin_user()
:@app.route('/login', methods=['get', 'post']) def login(): form = LoginForm() if form.validate_on_submit(): user=Usuarios.query.filter_by(username=form.username.data).first() if user!=None and user.verify_password(form.password.data): login_user(user) return redirect(url_for('inicio')) form.username.errors.append("Usuario o contraseña incorrectas.") return render_template('login.html', form=form)
-
También hemos creado una ruta
logout
que nos permite al usuario terminar la sesión utilizando la función `logout_user():@app.route("/logout") def logout(): logout_user() return redirect(url_for('login'))
En la siguiente unidad veremos como posibilitar que los usuarios se registren en nuestra aplicación, creando nuevos usuarios y posteriormente veremos como autorizar las distintas operaciones que puede realizar un usuario según su rol.