📅  最后修改于: 2023-12-03 14:56:02.683000             🧑  作者: Mango
本文将介绍如何用 Python 开发一种基于烧瓶(Flask)的身份验证系统,而不必使用数据库存储用户信息。该系统使用基于文件的用户存储系统,它可以轻松地扩展到存储更多用户,而且运行速度相当快。
身份验证系统是一个常见的功能,它通常涉及到创建用户,登录和鉴权等问题。在传统的身份验证系统中,通常使用关系型数据库存储用户信息。但是,考虑到简单性,我们将使用基于文件的用户存储系统代替数据库。
具体实现思路如下:
本身份验证系统实现了以下功能:
.
├── app.py # Flask 应用程序
└── users/ # 存储用户信息的目录
└── alice # 用户 Alice 的信息
└── password # Alice 的密码散列值
首先,我们需要创建一个 Flask 应用程序,并添加需要的路由。
from flask import Flask, jsonify, request
from flask_jwt import JWT, jwt_required, current_identity
from werkzeug.security import safe_str_cmp
app = Flask(__name__)
app.config['SECRET_KEY'] = 'super-secret'
jwt = JWT(app, lambda username: identity(username))
class User:
def __init__(self, id, username, password):
self.id = id
self.username = username
self.password = password
def __str__(self):
return f'User(id={self.id}, username={self.username})'
def identity(payload):
username = payload['identity']
user = get_user(username)
return user
def get_user(username):
user_file = f'users/{username}/password'
try:
with open(user_file, 'r') as f:
password_hash = f.readline().strip()
return User(1, username, password_hash)
except FileNotFoundError:
return None
@app.route('/login', methods=['POST'])
def login():
username = request.json.get('username', None)
password = request.json.get('password', None)
user = get_user(username)
if user and safe_str_cmp(user.password.encode('utf-8'), password.encode('utf-8')):
token = jwt.jwt_encode_callback(user)
return jsonify({'token': token.decode('utf-8')})
else:
return jsonify({'error': 'Invalid username or password'}), 401
@app.route('/protected')
@jwt_required()
def protected():
return jsonify({'message': f'Hello, {current_identity.username}!'})
在创建 Flask 应用程序时,我们指定了一个秘密 key。该 key 用于生成 token,也可以用于加密会话 cookie。
我们使用 Flask-JWT 扩展来实现 token 鉴权。该扩展提供了一个装饰器 jwt_required
,用于保护需要身份验证的路由。
get_user
函数将会根据用户名返回用户对象,如果找不到指定用户名的用户,则该函数返回 None。
在登录路由中,我们首先从请求中获取用户名和密码。如果存在指定用户名的用户,且密码正确,则生成一个 token 并返回。否则,返回错误消息。
在访问保护资源的路由中,我们使用 jwt_required
装饰器,以确保只有携带有效 token 的用户才能访问该路由。如果用户的身份验证通过,我们将返回一个消息,其中包含登录用户的用户名信息。
在实现用户账户的创建时,需要将用户的信息散列化以后存储到文件中。
import hashlib
import os
def add_user(username, password):
password_hash = hashlib.sha256(password.encode('utf-8')).hexdigest()
user_dir = f'users/{username}'
if not os.path.exists(user_dir):
os.makedirs(user_dir)
password_file = f'{user_dir}/password'
with open(password_file, 'w') as f:
f.write(password_hash)
在添加新用户时,我们首先根据用户提供的密码计算出密码散列值,并将该值存储到文件中。为了尽可能地增强对抗密码破解的能力,我们使用了 SHA256 算法来计算密码散列值。
我们可以通过以下代码来使用 Flask 内置的 Web 服务器运行我们的应用程序:
if __name__ == '__main__':
app.run(debug=True)
该参数会启动debug模式,以便在代码中出现错误时,能准确地获取错误信息。
在测试该应用程序时,我们需要使用 HTTPie 或 Postman 等 HTTP 客户端,以向应用程序发送请求并获取响应。首先,我们需要使用用户名和密码创建一个新的用户:
http POST localhost:5000/users username=alice password=secret
该请求将会发送一个 POST 请求,参数为 username 和 password,依据响应是否 200 来判断是否创建成功;
接下来,我们可以使用该用户的用户名和密码来登录并获取 token:
http POST localhost:5000/login username=alice password=secret
该请求将会发送一个 POST 请求,参数为 username 和 password,响应为 token;
最后,我们可以使用该 token 来访问保护资源:
http GET localhost:5000/protected Authorization:Bearer ${TOKEN}
该请求将会发送一个 GET 请求,参数为 Authorization,用于获取保护资源的token;
如果 token 有效,则将收到一个与以下类似的响应:
{
"message": "Hello, alice!"
}
在本文中,我们介绍了如何使用 Flask 和基于文件的用户存储系统来实现一种简单的身份验证系统。该系统具有良好的扩展性和运行速度。在实践中,我们可以根据具体的业务需求以及安全需求,对该系统进行进一步的定制和优化。