Introducción
La autenticación es el proceso de verificar las credenciales del usuario antes de otorgar acceso a una aplicación. Para iniciar sesión en una aplicación, los usuarios finales ingresan sus nombres de usuario y contraseñas. Debajo del capó, un proceso en segundo plano compara las credenciales de los usuarios con los valores de la base de datos para verificar si hay una coincidencia.
Todo el proceso de autenticación requiere un viaje de ida y vuelta a una base de datos basada en disco como PostgreSQL cada vez que un usuario accede a la aplicación. Cuando crece la base de usuarios de la aplicación, las bases de datos basadas en disco encuentran problemas de escalabilidad. Para superar el desafío, aquí es donde entra en juego una base de datos en memoria como Redis.
Puede usar la base de datos de Redis para almacenar en caché los detalles de autenticación cuando un usuario inicia sesión en una aplicación por primera vez. Luego, durante las siguientes solicitudes, puede consultar el servidor Redis para verificar el estado de autenticación en lugar de acceder a la base de datos basada en disco. Redis es varias veces más rápido que las bases de datos basadas en disco. Este enfoque hace que su aplicación sea más rápida y escalable al final.
Esta guía describe el proceso de autenticación de una aplicación Python con bases de datos PostgreSQL y Redis administradas desde la plataforma Vultr. Vultr proporciona una base de datos administrada segura y altamente escalable que funciona de inmediato para automatizar todas las tareas difíciles de la administración de su base de datos.
requisitos previos
Para seguir esta guía:
-
Implemente un servidor Ubuntu 20.04.
-
Crear un no root sudo usuario.
-
Aprovisione un clúster de base de datos administrado de PostgreSQL y uno de Redis. Utilice la misma ubicación para ambos clústeres.
-
Localiza el Detalles de conexión para cada base de datos bajo el Visión general pestaña. Esta guía utiliza los siguientes detalles de conexión de muestra:
-
Servidor Redis:
-
nombre de usuario :
default
-
clave :
EXAMPLE_REDIS_PASSWORD
-
anfitrión :
SAMPLE_REDIS_DB_HOST_STRING.vultrdb.com
-
Puerto :
16752
-
-
Servidor PostgreSQL:
-
nombre de usuario :
vultradmin
-
clave :
EXAMPLE_POSTGRESQL_PASSWORD
-
anfitrión :
SAMPLE_POSTGRESQL_DB_HOST_STRING.vultrdb.com
-
Puerto :
16751
-
-
1. Configurar una base de datos de muestra
Esta guía utiliza la base de datos PostgreSQL administrada para almacenar datos de forma permanente en un disco. Para esta aplicación de ejemplo, necesita una base de datos y dos tablas. La primera mesa almacena productos. Luego, un script de Python consulta la tabla para devolver los productos en formato JSON cuando los usuarios envían solicitudes a la aplicación. La segunda tabla almacena usuarios y sus credenciales de autenticación. Siga los pasos a continuación para configurar la base de datos:
-
Actualice el índice de información del paquete.
$ sudo apt update
-
Instala el
postgresql-client
paquete. Debido a que esta aplicación utiliza la base de datos administrada por PostgreSQL de Vultr, solo necesita el cliente de línea de comandos de PostgreSQL para consultar la base de datos.$ sudo apt install -y postgresql-client
-
Utilizar el
psql
Comando para iniciar sesión en la base de datos PostgreSQL administrada. ReemplazarSAMPLE_POSTGRESQL_DB_HOST_STRING.vultrdb.com
con el nombre correcto delhost
.$ psql -h SAMPLE_POSTGRESQL_DB_HOST_STRING.vultrdb.com -p 16751 -U vultradmin defaultdb
-
Asegúrese de obtener la siguiente solicitud de contraseña.
Password for user vultradmin:
-
Enter la contraseña para el usuario de PostgreSQL administrado y presione ENTER para continuar. Luego, verifique el siguiente resultado.
defaultdb=>
-
Enter el siguiente comando para crear una muestra
my_company
base de datos.defaultdb=> CREATE DATABASE my_company;
Producción.
CREATE DATABASE
-
Cambiar a lo nuevo
my_company
base de datos.defaultdb=> c my_company;
Producción.
You are now connected to database "my_company" as user "vultradmin". my_company=>
-
Crear un
products
mesa. Esta guía utiliza una sola tabla. En un entorno de producción, puede tener decenas o cientos de tablas según la complejidad de su aplicación.my_company=> CREATE TABLE products ( product_id SERIAL PRIMARY KEY, product_name VARCHAR (50), retail_price NUMERIC(5, 2) );
Producción.
CREATE TABLE
-
Rellenar el
products
mesa.my_company=> INSERT INTO products (product_name, retail_price) VALUES ('1L FOUNTAIN DRINKING WATER', 2.55); INSERT INTO products (product_name, retail_price) VALUES ('PINK COTTON BUDS', 4.85); INSERT INTO products (product_name, retail_price) VALUES ('WINE GLASS', 9.75);
Producción.
... INSERT 0 1
-
consulta el
products
tabla para asegurarse de que los datos están en su lugar.my_company=> SELECT product_id, product_name, retail_price FROM products;
Producción.
product_id | product_name | retail_price ------------+----------------------------+-------------- 1 | 1L FOUNTAIN DRINKING WATER | 2.55 2 | PINK COTTON BUDS | 4.85 3 | WINE GLASS | 9.75 (3 rows)
-
Crear un
users
mesa. losusers
tabla almacena información de los usuarios, comouser_id
,username
ypwd
(clave).my_company=> CREATE TABLE users ( user_id SERIAL PRIMARY KEY, username VARCHAR (50), pwd VARCHAR (255) );
Producción.
CREATE TABLE
-
Ejecute el siguiente comando para habilitar el
pgcrypto
extensión. Necesita esta extensión para cifrar las contraseñas antes de insertarlas en elusers
mesa.my_company=> CREATE EXTENSION pgcrypto;
Producción.
CREATE EXTENSION
-
Rellenar el
users
tabla con datos de ejemplo. Esta guía utilizaEXAMPLE_PASSWORD
yEXAMPLE_PASSWORD_2
. Recuerde utilizar contraseñas seguras para evitar ataques de fuerza bruta en un entorno de producción.my_company=> INSERT INTO users (username, pwd) VALUES ('john_doe', crypt('EXAMPLE_PASSWORD', gen_salt('bf'))); INSERT INTO users (username, pwd) VALUES ('mary_smith', crypt('EXAMPLE_PASSWORD_2', gen_salt('bf')));
Producción.
... INSERT 0 1
-
consulta el
users
mesa para verificar los registros y el funcionamiento de lospgcrypto
extensión.my_company=> SELECT user_id, username, pwd FROM users;
Producción.
user_id | username | pwd ---------+------------+-------------------------------------------------------------- 1 | john_doe | $2a$06$spijfwl34nCdBpApp1C68OWa//j0buReiQ4SHAJVCV4sm627iyyZW 2 | mary_smith | $2a$06$g6FjH7PXSCMT75uIKB94ZOUWHbeth0SsHebOqcykjXM4Dq6mtlxtG (2 rows)
-
Cierre sesión en el servidor PostgreSQL administrado.
my_company=> q
-
Continúe con el siguiente paso para crear una clase de base de datos para el servidor PostgreSQL.
2. Crear una clase de base de datos PostgreSQL
Este paso le muestra cómo crear una clase central de PostgreSQL que puede usar desde su aplicación para acceder a las funciones de la base de datos. Siga los pasos a continuación para crear la clase:
-
Crear un
project
directorio para separar su código fuente de los archivos del sistema.$ mkdir project
-
Cambiar a lo nuevo
project
directorio.$ cd project
-
abrir un nuevo
posgresql_gateway.py
archivo en un editor de texto.$ nano postgresql_gateway.py
-
Enter la siguiente información en el
postgresql_gateway.py
expediente. Reemplace ladb_pass
ydb_host
valores con la correctahost
ypassword
para la base de datos PostgreSQL administrada.import psycopg2 import bcrypt class PostgresqlGateway: def __init__(self): db_host="SAMPLE_POSTGRESQL_DB_HOST_STRING.vultrdb.com" db_port = 16751 db_name="my_company" db_user="vultradmin" db_pass="EXAMPLE_POSTGRESQL_PASSWORD" self.postgresql_client = psycopg2.connect(host = db_host, database = db_name, user = db_user, password = db_pass, port = db_port) def get_products(self): sql_string = 'select product_id, product_name, retail_price from products' cur = self.postgresql_client.cursor() cur.execute(sql_string) rows = cur.fetchall() products = [] dt_columns = list(cur.description) for row in rows: row_data = {} for i, col in enumerate(dt_columns): row_data[col.name] = str(row[i]) products.append(row_data) return products def authenticate_user(self, username, password): sql_string = "select username, pwd from users where username = %s" cur = self.postgresql_client.cursor() cur.execute(sql_string, (username,)) if cur.rowcount < 1 : return False else: row = cur.fetchone() if bcrypt.checkpw(password.encode('utf8'), row[1].encode('utf8')): self.hashed_password = row[1].encode('utf8') return True else: return False
-
Guardar y close la
postgresql_gateway.py
expediente.
los postgresql_gateway.py
archivo explicado:
-
los
import
sección declara dos bibliotecas. lospsycopg2
es una biblioteca popular de Python para la base de datos PostgreSQL. losbcrypt
es una biblioteca de hashing de contraseñas.import psycopg2 import bcrypt ...
-
los
PostgresqlGateway
La clase tiene tres métodos.class PostgresqlGateway: def __init__(self): ... def get_products(self): ... def authenticate_user(self, username, password): ...
-
los
_init_()
El método establece una conexión de base de datos a la base de datos PostgreSQL cuando crea una instancia de la clase. -
los
get_products(...)
El método consulta elproducts
tabla para recuperar una lista de productos de la base de datos. -
los
authenticate_user(...)
El método consulta elusers
table para encontrar una coincidencia cuando un usuario intenta iniciar sesión en la aplicación. Si las credenciales de un usuario coinciden con un registro en elusers
tabla, el método authenticate_user devuelveTrue
. -
los
if bcrypt.checkpw(password.encode('utf8'), row[1].encode('utf8')):
declaración compara la contraseña del usuario con el valor de la base de datos utilizando elbcrypt
biblioteca.
los postgresql_gateway.py
la clase ya está lista. Para usarlo en otros archivos de Python, use la siguiente sintaxis:
import postgresql_gateway
pg = postgresql_gateway.PostgresqlGateway()
... = pg.get_products()
... = pg.authenticate_user(username, password)
Siga el siguiente paso para crear una clase de base de datos de Redis.
3. Crear una clase de base de datos Redis
Este paso se centra en la creación de una clase de base de datos de Redis. La clase proporciona funcionalidades de Redis para crear y recuperar claves. Ejecute los pasos a continuación para crear la clase:
-
abrir un nuevo
redis_gateway.py
archivo en un editor de texto.$ nano redis_gateway.py
-
Enter la siguiente información en el
redis_gateway.py
expediente. Reemplace ladb_host
ydb_pass
valores con la correctahost
ypassword
desde su servidor Redis administrado.import redis import bcrypt class RedisGateway: def __init__(self): db_host="SAMPLE_REDIS_DB_HOST_STRING.vultrdb.com" db_port = 16752 db_pass="EXAMPLE_REDIS_PASSWORD" self.redis_client = redis.Redis(host = db_host, port = db_port, password = db_pass, ssl="true") def cache_user(self, username, password): self.redis_client.set(username, password) def authenticate_user(self, username, password): if self.redis_client.exists(username): hashed_password = self.redis_client.get(username) if bcrypt.checkpw(password.encode('utf8'), hashed_password): return True else: return False
-
Guardar y close la
redis_gateway.py
expediente.
los redis_gateway.py
archivo explicado:
-
los
import
La sección declara dos bibliotecas de Python. losredis
biblioteca proporciona una interfaz entre Python y el servidor Redis administrado. losbcrypt
La biblioteca compara la contraseña de texto sin formato proporcionada por un usuario y el pase hash de Redis.... import redis import bcrypt
-
los
RedisGateway
La clase tiene tres métodos.... class RedisGateway: def __init__(self): ... def cache_user(self, username, password): ... def authenticate_user(self, username, password): ...
-
los
_init_()
El método establece una conexión con la base de datos de Redis administrada. -
los
cache_user()
El método guarda los detalles de autenticación del usuario en el servidor Redis usando elself.redis_client.set(username, password)
función. Cada usuario tiene un únicousername
que actúa como una clave Redis mientras que elpassword
es un valor de Redis. -
los
authenticate_user(...)
El método consulta al servidor Redis para verificar si una clave (hashed_password
) nombrado con el dadousername
existe usando elif self.redis_client.exists(username):
declaración. Si la contraseña del usuario está disponible en el servidor de Redis, elauthenticate_user(...)
función devuelveTrue
. De lo contrario, la función devuelveFalse
.
los RedisGateway
la clase ya está lista. Puede importar y usar la clase en otros archivos de Python usando la siguiente sintaxis:
import redis_gateway
rg = redis_gateway.RedisGateway()
... = pg.authenticate_user(username, password)
rg.cache_user(username, pg.hashed_password)
Siga el siguiente paso para terminar de codificar su aplicación.
4. Crear el punto de entrada de la aplicación
El paso final es crear un punto de entrada a la aplicación de muestra. Esta guía utiliza un main.py
archivo como el archivo de inicio de la aplicación. Siga los pasos a continuación para crear el archivo:
-
abrir un nuevo
main.py
archivo en un editor de texto.$ nano main.py
-
Enter la siguiente información en el
main.py
expediente.import http.server from http import HTTPStatus import socketserver import json import base64 import postgresql_gateway import redis_gateway class httpHandler(http.server.SimpleHTTPRequestHandler): def do_GET(self): authHeader = self.headers.get('Authorization').split(' '); username, password = base64.b64decode(authHeader[1]).decode('utf8').split(':') self.send_response(HTTPStatus.OK) self.send_header('Content-type', 'application/json') self.end_headers() pg = postgresql_gateway.PostgresqlGateway() rg = redis_gateway.RedisGateway() data = dict() if rg.authenticate_user(username, password) == True: products = pg.get_products() data = {'authenticated_by' : 'Redis Server', 'data': products} else: if pg.authenticate_user(username, password) == True: rg.cache_user(username, pg.hashed_password) products = pg.get_products() data = {'authenticated_by' : 'PostgreSQL Server', 'data': products} else: data = {'error': 'Authentication failed.'} resp = json.dumps(data, indent = 4, separators = (',', ': ')) self.wfile.write(bytes(resp + 'rn', "utf8")) httpServer = socketserver.TCPServer(('', 8080), httpHandler) print("HTTP server started at port 8080...") try: httpServer.serve_forever() except KeyboardInterrupt: httpServer.server_close() print("The server is stopped.")
-
Guardar y close la
main.py
expediente.
los main.py
archivo explicado:
-
los
import
sección declara el servidor HTTP (http.server
,HTTPStatus
ysocketserver
),json
,base64
,postgresql_gateway
yredis_gateway
bibliotecasimport http.server from http import HTTPStatus import socketserver import json import base64 import postgresql_gateway import redis_gateway ...
-
los
httpHandler
es una clase de controlador HTTP para la aplicación con unado_GET(self)
método. Este método se activa cuando un usuario envía unGET
petición a la aplicación. losdo_GET
El método genera una salida JSON.class httpHandler(http.server.SimpleHTTPRequestHandler): def do_GET(self): ... resp = json.dumps(data, indent = 4, separators = (',', ': ')) self.wfile.write(bytes(resp + 'rn', "utf8"))
-
los
do_GET()
El método declara las dos bibliotecas PostgreSQL y Redis personalizadas que creó anteriormente con la siguiente sintaxis.pg = postgresql_gateway.PostgresqlGateway() rg = redis_gateway.RedisGateway()
-
La lógica principal de la aplicación radica en el siguiente código.
... if rg.authenticate_user(username, password) == True: products = pg.get_products() data = {'authenticated_by' : 'Redis Server', 'data': products} else: if pg.authenticate_user(username, password) == True: rg.cache_user(username, pg.hashed_password) products = pg.get_products() data = {'authenticated_by' : 'PostgreSQL Server', 'data': products} else: data = {'error': 'Authentication failed.'} ...
-
los
rg.authenticate_user(username, password) == True:
logic consulta al servidor de Redis para verificar si los detalles del usuario ya están almacenados en caché. Si la función devuelveTrue
la lógica llama a laproducts = pg.get_products()
para generar los productos de la base de datos PostgreSQL. -
Si los detalles del usuario no se encuentran en el servidor Redis, el
if pg.authenticate_user(username, password) == True:
logic busca las credenciales del usuario en la base de datos PostgreSQL. Si los detalles del usuario son correctos, la lógica llama alrg.cache_user(username, pg.hashed_password)
para almacenar en caché los detalles del usuario en el servidor de Redis para otras llamadas y luego ejecuta elpg.get_products()
función para generar los productos de la base de datos PostgreSQL. -
Las declaraciones
{'authenticated_by' : 'Redis Server', 'data': products}
y{'authenticated_by' : 'PostgreSQL Server', 'data': products}
le permiten identificar cómo el usuario se autentica en la aplicación. Esto es solo para fines de demostración, y puede eliminar elauthenticated_by
valores en un entorno de producción. -
La siguiente declaración inicia un servidor web que escucha las conexiones entrantes en el puerto
8080
y declara lahttpHandler
función como la función del controlador.... httpServer = socketserver.TCPServer(('', 8080), httpHandler) print("HTTP server started at port 8080...") try: httpServer.serve_forever() except KeyboardInterrupt: httpServer.server_close() print("The server is stopped.")
Su aplicación ahora está lista para ser probada.
5. Pruebe la lógica de la aplicación
El paso final es instalar todas las bibliotecas de terceros requeridas por la aplicación y probar la lógica de autenticación. Siga los pasos a continuación para completar esos pasos:
-
Instalar el pitón
pip
paquete.$ sudo apt install -y python3-pip
-
Utilizar el
pip
paquete para instalar elpsycopg2
módulo. Para pruebas y desarrollo, use el paquete binario (psycopg2-binary
). Sin embargo, en un entorno de producción, considere usar elpsycopg2
paquete.$ pip install psycopg2-binary
Producción.
... Successfully installed psycopg2-binary-2.9.5
-
Instala el
redis
módulo para Python.$ pip install redis
Producción.
... Successfully installed async-timeout-4.0.2 packaging-21.3 pyparsing-3.0.9 redis-4.3.5
-
Instala el
bcrypt
módulo para Python.$ pip install bcrypt
Producción.
... Successfully installed bcrypt-4.0.1
-
Utilizar el
python3
comando para ejecutar la aplicación.$ python3 main.py
Producción.
HTTP server started at port 8080...
-
Establezca otra conexión SSH a su servidor y emita el siguiente Linux
curl
comandos para enviar dosGET
solicitudes a la aplicación.-
john_doe
:$ curl -X GET -u john_doe:EXAMPLE_PASSWORD https://localhost:8080/ $ curl -X GET -u john_doe:EXAMPLE_PASSWORD https://localhost:8080/
-
mary_smith
:ps curl -X GET -u marysmith:EXAMPLEPASSWORD_2 https://localhost:8080/
ps curl -X GET -u marysmith:EXAMPLEPASSWORD_2 https://localhost:8080/
-
-
Tenga en cuenta las siguientes salidas. En la primera salida, el
authenticated_by
lecturas de valorPostgreSQL Server
. Sin embargo, en la segunda solicitud, elauthenticated_by
lecturas de valorRedis Server
.Salida 1.
... { "authenticated_by": "PostgreSQL Server", "data": [ { "product_id": "1", "product_name": "1L FOUNTAIN DRINKING WATER", "retail_price": "2.55" }, { "product_id": "2", "product_name": "PINK COTTON BUDS", "retail_price": "4.85" }, { "product_id": "3", "product_name": "WINE GLASS", "retail_price": "9.75" } ] }
Salida 2.
... { "authenticated_by": "Redis Server", "data": [ { "product_id": "1", "product_name": "1L FOUNTAIN DRINKING WATER", "retail_price": "2.55" }, { "product_id": "2", "product_name": "PINK COTTON BUDS", "retail_price": "4.85" }, { "product_id": "3", "product_name": "WINE GLASS", "retail_price": "9.75" } ] }
La lógica de su aplicación funciona como se esperaba.
Conclusión
Esta guía utiliza las bases de datos Redis y PostgreSQL administradas por Vultr para acelerar la autenticación de una aplicación de Python en el servidor Ubuntu 20.04. Utilice los archivos de código fuente de muestra de esta guía para escalar su aplicación en su próximo proyecto de Python.
Lea más guías sobre el servidor Redis siguiendo los enlaces a continuación:
-
Implemente un carrito de compras en Python con una base de datos Redis administrada por Vultr.
-
Cómo conectarse de forma segura a Redis con TLS/SSL en Go, NodeJS, PHP, Python y redis-cli.
-
Cómo usar una caché de Redis con PostgreSQL en Golang
Título del artículo Nombre (opcional) Correo electrónico (opcional) Descripción
Enviar sugerencia