介绍
身份验证是在授予对应用程序的访问权限之前验证用户凭据的过程。 要登录应用程序,最终用户需要输入他们的用户名和密码。 在幕后,后台进程将用户的凭据与数据库值进行比较,以检查是否存在匹配项。
每次用户访问应用程序时,整个身份验证过程都需要往返于基于磁盘的数据库(如 PostgreSQL)。 当应用程序的用户群增长时,基于磁盘的数据库会遇到可伸缩性问题。 为了克服挑战,这就是像 Redis 这样的内存数据库发挥作用的地方。
当用户首次登录应用程序时,您可以使用 Redis 数据库缓存身份验证详细信息。 然后,在以下请求期间,您可以查询 Redis 服务器以检查身份验证状态,而不是访问基于磁盘的数据库。 Redis 比基于磁盘的数据库快几倍。 这种方法最终使您的应用程序更快,更具可扩展性。
本指南描述了使用来自 Vultr 平台的托管 PostgreSQL 和 Redis 数据库对 Python 应用程序进行身份验证的过程。 Vultr 提供了一个安全且高度可扩展的托管数据库,开箱即用,可以自动执行数据库管理的所有困难任务。
先决条件
要遵循本指南:
部署 Ubuntu 20.04 服务器。
创建非root sudo 用户。
提供一个 PostgreSQL 和一个 Redis 托管数据库集群。 对两个集群使用相同的位置。
找到 连接细节 对于下面的每个数据库 概述 标签。 本指南使用以下示例连接详细信息:
雷迪斯服务器:
用户名:
default
密码:
EXAMPLE_REDIS_PASSWORD
主持人:
SAMPLE_REDIS_DB_HOST_STRING.vultrdb.com
港口:
16752
PostgreSQL 服务器:
用户名:
vultradmin
密码:
EXAMPLE_POSTGRESQL_PASSWORD
主持人:
SAMPLE_POSTGRESQL_DB_HOST_STRING.vultrdb.com
港口:
16751
1. 建立样本数据库
本指南使用托管 PostgreSQL 数据库将数据永久存储在磁盘上。 对于这个示例应用程序,您需要一个数据库和两个表。 第一个表存储产品。 然后,当用户向应用程序发送请求时,Python 脚本会查询该表以返回 JSON 格式的产品。 第二个表存储用户及其身份验证凭据。 按照以下步骤设置数据库:
更新包信息索引。
$ sudo apt update
安装
postgresql-client
包裹。 因为此应用程序使用来自 Vultr 的 PostgreSQL 管理的数据库,所以您只需要 PostgreSQL 命令行客户端来查询数据库。$ sudo apt install -y postgresql-client
使用
psql
命令登录到托管的 PostgreSQL 数据库。 代替SAMPLE_POSTGRESQL_DB_HOST_STRING.vultrdb.com
用正确的名字host
.$ psql -h SAMPLE_POSTGRESQL_DB_HOST_STRING.vultrdb.com -p 16751 -U vultradmin defaultdb
确保您收到以下密码提示。
Password for user vultradmin:
Enter 托管 PostgreSQL 用户的密码,然后按 ENTER 继续。 然后,验证以下输出。
defaultdb=>
Enter 以下命令创建示例
my_company
数据库。defaultdb=> CREATE DATABASE my_company;
输出。
CREATE DATABASE
切换到新的
my_company
数据库。defaultdb=> c my_company;
输出。
You are now connected to database "my_company" as user "vultradmin". my_company=>
创建一个
products
桌子。 本指南使用单个表。 在生产环境中,您可能有数十个或数百个表,具体取决于应用程序的复杂性。my_company=> CREATE TABLE products ( product_id SERIAL PRIMARY KEY, product_name VARCHAR (50), retail_price NUMERIC(5, 2) );
输出。
CREATE TABLE
填充
products
桌子。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);
输出。
... INSERT 0 1
查询
products
表以确保数据到位。my_company=> SELECT product_id, product_name, retail_price FROM products;
输出。
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)
创建一个
users
桌子。 这users
表存储用户的信息,例如user_id
,username
, 和pwd
(密码)。my_company=> CREATE TABLE users ( user_id SERIAL PRIMARY KEY, username VARCHAR (50), pwd VARCHAR (255) );
输出。
CREATE TABLE
发出以下命令以启用
pgcrypto
扩大。 在将密码插入到users
桌子。my_company=> CREATE EXTENSION pgcrypto;
输出。
CREATE EXTENSION
填充
users
带有样本数据的表格。 本指南使用EXAMPLE_PASSWORD
和EXAMPLE_PASSWORD_2
. 请记住使用强密码来防止生产环境中的暴力攻击。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')));
输出。
... INSERT 0 1
查询
users
核实记录和工作情况的表格pgcrypto
扩大。my_company=> SELECT user_id, username, pwd FROM users;
输出。
user_id | username | pwd ---------+------------+-------------------------------------------------------------- 1 | john_doe | $2a$06$spijfwl34nCdBpApp1C68OWa//j0buReiQ4SHAJVCV4sm627iyyZW 2 | mary_smith | $2a$06$g6FjH7PXSCMT75uIKB94ZOUWHbeth0SsHebOqcykjXM4Dq6mtlxtG (2 rows)
从受管 PostgreSQL 服务器注销。
my_company=> q
继续下一步,为 PostgreSQL 服务器创建数据库类。
2. 创建 PostgreSQL 数据库类
此步骤向您展示怎样创建一个中央 PostgreSQL 类,您可以在应用程序中使用该类来访问数据库功能。 按照以下步骤创建类:
创建一个
project
用于将源代码与系统文件分开的目录。$ mkdir project
切换到新的
project
目录。$ cd project
开一个新的
posgresql_gateway.py
文本编辑器中的文件。$ nano postgresql_gateway.py
Enter 将以下信息放入
postgresql_gateway.py
文件。 更换db_pass
和db_host
具有正确值host
和password
对于托管的 PostgreSQL 数据库。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
Save 和 close 这
postgresql_gateway.py
文件。
这 postgresql_gateway.py
文件解释:
这
import
部分声明了两个库。 这psycopg2
是用于 PostgreSQL 数据库的流行 Python 库。 这bcrypt
是一个密码哈希库。import psycopg2 import bcrypt ...
这
PostgresqlGateway
类具有三个方法。class PostgresqlGateway: def __init__(self): ... def get_products(self): ... def authenticate_user(self, username, password): ...
这
_init_()
方法在实例化类时建立到 PostgreSQL 数据库的数据库连接。这
get_products(...)
方法查询products
表以从数据库中检索产品列表。这
authenticate_user(...)
方法查询users
表以在用户尝试登录应用程序时查找匹配项。 如果用户的凭据与中的记录匹配users
表, authenticate_user 方法返回True
.这
if bcrypt.checkpw(password.encode('utf8'), row[1].encode('utf8')):
语句将用户密码与数据库值进行比较bcrypt
图书馆。
这 postgresql_gateway.py
类现在准备好了。 要在其他 Python 文件中使用它,请使用以下语法:
import postgresql_gateway
pg = postgresql_gateway.PostgresqlGateway()
... = pg.get_products()
... = pg.authenticate_user(username, password)
按照下一步创建 Redis 数据库类。
3.创建Redis数据库类
此步骤重点创建 Redis 数据库类。 该类提供用于创建和检索密钥的 Redis 功能。 执行以下步骤创建类:
开一个新的
redis_gateway.py
文本编辑器中的文件。$ nano redis_gateway.py
Enter 将以下信息放入
redis_gateway.py
文件。 更换db_host
和db_pass
具有正确值host
和password
从您的托管 Redis 服务器。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
Save 和 close 这
redis_gateway.py
文件。
这 redis_gateway.py
文件解释:
这
import
部分声明了两个 Python 库。 这redis
库提供了 Python 和托管的 Redis 服务器之间的接口。 这bcrypt
库比较用户提供的纯文本密码和来自 Redis 的散列密码。... import redis import bcrypt
这
RedisGateway
类具有三个方法。... class RedisGateway: def __init__(self): ... def cache_user(self, username, password): ... def authenticate_user(self, username, password): ...
这
_init_()
方法建立与托管 Redis 数据库的连接。这
cache_user()
方法使用self.redis_client.set(username, password)
功能。 每个用户都有一个独一无二的username
充当 Redis 键,而password
是一个 Redis 值。这
authenticate_user(...)
方法查询 Redis 服务器以检查键 (hashed_password
) 以给定命名username
存在使用if self.redis_client.exists(username):
陈述。 如果用户的密码可从 Redis 服务器获得,则authenticate_user(...)
函数返回True
. 否则,函数返回False
.
这 RedisGateway
类现在准备好了。 您可以使用以下语法在其他 Python 文件中导入和使用该类:
import redis_gateway
rg = redis_gateway.RedisGateway()
... = pg.authenticate_user(username, password)
rg.cache_user(username, pg.hashed_password)
按照下一步完成应用程序的编码。
4. 创建应用程序的入口点
最后一步是创建示例应用程序的入口点。 本指南使用 main.py
文件作为应用程序的启动文件。 按照以下步骤创建文件:
开一个新的
main.py
文本编辑器中的文件。$ nano main.py
Enter 将以下信息放入
main.py
文件。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.")
Save 和 close 这
main.py
文件。
这 main.py
文件解释:
这
import
部分声明 HTTP 服务器 (http.server
,HTTPStatus
, 和socketserver
),json
,base64
,postgresql_gateway
, 和redis_gateway
图书馆。import http.server from http import HTTPStatus import socketserver import json import base64 import postgresql_gateway import redis_gateway ...
这
httpHandler
是一个应用程序的 HTTP 处理程序类do_GET(self)
方法。 当用户发送一个GET
向应用程序请求。 这do_GET
方法输出 JSON 输出。class httpHandler(http.server.SimpleHTTPRequestHandler): def do_GET(self): ... resp = json.dumps(data, indent = 4, separators = (',', ': ')) self.wfile.write(bytes(resp + 'rn', "utf8"))
这
do_GET()
方法声明您之前使用以下语法创建的两个自定义 PostgreSQL 和 Redis 库。pg = postgresql_gateway.PostgresqlGateway() rg = redis_gateway.RedisGateway()
该应用程序的主要逻辑在于以下代码。
... 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.'} ...
这
rg.authenticate_user(username, password) == True:
逻辑查询Redis服务器检查用户的详细信息是否已经缓存。 如果函数返回True
,逻辑调用products = pg.get_products()
从 PostgreSQL 数据库输出产品。如果在 Redis 服务器上找不到用户的详细信息,则
if pg.authenticate_user(username, password) == True:
逻辑从 PostgreSQL 数据库中查找用户的凭据。 如果用户详细信息正确,则逻辑调用rg.cache_user(username, pg.hashed_password)
将用户的详细信息缓存到 Redis 服务器以供其他调用,然后运行pg.get_products()
函数从 PostgreSQL 数据库输出产品。声明
{'authenticated_by' : 'Redis Server', 'data': products}
和{'authenticated_by' : 'PostgreSQL Server', 'data': products}
允许您确定用户怎样向应用程序进行身份验证。 这仅用于演示目的,您可以删除authenticated_by
生产环境中的值。下面的语句启动一个 web 服务器,它监听端口上的传入连接
8080
并宣布httpHandler
充当处理函数。... 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.")
您的应用程序现在可以进行测试了。
5. 测试应用逻辑
最后一步是安装应用程序所需的所有第三方库并测试身份验证逻辑。 请按照以下步骤完成这些步骤:
安装 Python
pip
包裹。$ sudo apt install -y python3-pip
使用
pip
安装包psycopg2
模块。 对于测试和开发,使用二进制包(psycopg2-binary
). 但是,在生产环境中,请考虑使用psycopg2
包裹。$ pip install psycopg2-binary
输出。
... Successfully installed psycopg2-binary-2.9.5
安装
redis
Python 模块。$ pip install redis
输出。
... Successfully installed async-timeout-4.0.2 packaging-21.3 pyparsing-3.0.9 redis-4.3.5
安装
bcrypt
Python 模块。$ pip install bcrypt
输出。
... Successfully installed bcrypt-4.0.1
使用
python3
命令运行应用程序。$ python3 main.py
输出。
HTTP server started at port 8080...
与您的服务器建立另一个 SSH 连接并发出以下 Linux
curl
命令发送两个GET
对应用程序的请求。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
:$ curl -X GET -u marysmith:EXAMPLEPASSWORD_2 https://localhost:8080/
$ curl -X GET -u marysmith:EXAMPLEPASSWORD_2 https://localhost:8080/
请注意以下输出。 在第一个输出中,
authenticated_by
值读取PostgreSQL Server
. 但是,在第二个请求中,authenticated_by
值读取Redis Server
.输出 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" } ] }
输出 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" } ] }
您的应用程序逻辑按预期工作。
结论
本指南使用 Vultr 的托管 Redis 和 PostgreSQL 数据库来加速在 Ubuntu 20.04 服务器上验证 Python 应用程序。 使用本指南的示例源代码文件在您的下一个 Python 项目中扩展您的应用程序。
通过以下链接阅读有关 Redis 服务器的更多指南:
使用 Vultr 托管的 Redis 数据库在 Python 中实现购物车。
怎样在 Go、NodeJS、PHP、Python 和 redis-cli 中使用 TLS/SSL 安全连接到 Redis。
怎样在 Golang 中使用 Redis 缓存和 PostgreSQL
文章标题 名称(可选) 电子邮件(可选) 描述
发送建议
注:本教程在Vultr VPS上测试通过,如需部署请前往Vultr.com