在 Flask 中使用 JWT 进行用户身份验证
先决条件: JSON Web Token (JWT) 基础知识
我将假设您具备 JWT 的基本知识以及 JWT 的工作原理。如果没有,那么我建议阅读链接的 Geeksforgeeks 文章。
让我们直接进入设置。当然,您需要在系统上安装python3 。现在,跟我来。我将使用一个虚拟环境来安装库,这无疑是进行任何类型开发的最佳方式。
- 首先创建一个名为flask project的文件夹并将目录更改为它。如果您使用的是 linux,请在终端中键入以下内容。
mkdir "flask project" && cd "flask project"
- 现在,创建一个虚拟环境。如果您使用的是 linux,请在终端中键入以下内容。
python3 -m venv env
注意:如果您收到任何错误,则表示您的系统中没有安装venv 。要安装它,请在终端中输入sudo apt install python3-venv ,然后就可以开始了。如果您在 Windows 上,则使用virtualenv 之类的东西来创建虚拟环境。
这将在烧瓶项目中创建一个名为venv的文件夹,其中将包含项目特定的库。
- 现在创建一个名为requirements.txt的文件并在其中添加以下行。
Flask-RESTful==0.3.8
PyJWT==1.7.1
Flask-SQLAlchemy==2.4.1
- 现在,让我们为这个项目安装这些库。为此,首先我们需要激活虚拟环境。为此,请在终端中键入以下内容。
source env/bin/activate
注意:如果您在 Windows 上,那么它将是Scripts而不是bin
现在,是时候安装这些库了。为此,请再次在终端中键入以下内容。
pip install -r requirements.txt
现在,我们完成了设置部分。现在让我们开始编写实际代码。在开始编写代码之前,我想澄清一些事情。我会将整个代码写在一个文件中,即数据库模型和路由放在一起,这不是一个好的做法,而且对于大型项目来说绝对无法管理。尝试为路由和数据库模型创建单独的Python文件或模块。
清除后,让我们直接开始编写实际代码。我将添加内联注释来解释代码的每个部分。
创建一个名为app.py的Python文件并在其中键入以下代码。
Python3
# flask imports
from flask import Flask, request, jsonify, make_response
from flask_sqlalchemy import SQLAlchemy
import uuid # for public id
from werkzeug.security import generate_password_hash, check_password_hash
# imports for PyJWT authentication
import jwt
from datetime import datetime, timedelta
from functools import wraps
# creates Flask object
app = Flask(__name__)
# configuration
# NEVER HARDCODE YOUR CONFIGURATION IN YOUR CODE
# INSTEAD CREATE A .env FILE AND STORE IN IT
app.config['SECRET_KEY'] = 'your secret key'
# database name
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///Database.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
# creates SQLALCHEMY object
db = SQLAlchemy(app)
# Database ORMs
class User(db.Model):
id = db.Column(db.Integer, primary_key = True)
public_id = db.Column(db.String(50), unique = True)
name = db.Column(db.String(100))
email = db.Column(db.String(70), unique = True)
password = db.Column(db.String(80))
# decorator for verifying the JWT
def token_required(f):
@wraps(f)
def decorated(*args, **kwargs):
token = None
# jwt is passed in the request header
if 'x-access-token' in request.headers:
token = request.headers['x-access-token']
# return 401 if token is not passed
if not token:
return jsonify({'message' : 'Token is missing !!'}), 401
try:
# decoding the payload to fetch the stored details
data = jwt.decode(token, app.config['SECRET_KEY'])
current_user = User.query\
.filter_by(public_id = data['public_id'])\
.first()
except:
return jsonify({
'message' : 'Token is invalid !!'
}), 401
# returns the current logged in users contex to the routes
return f(current_user, *args, **kwargs)
return decorated
# User Database Route
# this route sends back list of users users
@app.route('/user', methods =['GET'])
@token_required
def get_all_users(current_user):
# querying the database
# for all the entries in it
users = User.query.all()
# converting the query objects
# to list of jsons
output = []
for user in users:
# appending the user data json
# to the response list
output.append({
'public_id': user.public_id,
'name' : user.name,
'email' : user.email
})
return jsonify({'users': output})
# route for logging user in
@app.route('/login', methods =['POST'])
def login():
# creates dictionary of form data
auth = request.form
if not auth or not auth.get('email') or not auth.get('password'):
# returns 401 if any email or / and password is missing
return make_response(
'Could not verify',
401,
{'WWW-Authenticate' : 'Basic realm ="Login required !!"'}
)
user = User.query\
.filter_by(email = auth.get('email'))\
.first()
if not user:
# returns 401 if user does not exist
return make_response(
'Could not verify',
401,
{'WWW-Authenticate' : 'Basic realm ="User does not exist !!"'}
)
if check_password_hash(user.password, auth.get('password')):
# generates the JWT Token
token = jwt.encode({
'public_id': user.public_id,
'exp' : datetime.utcnow() + timedelta(minutes = 30)
}, app.config['SECRET_KEY'])
return make_response(jsonify({'token' : token.decode('UTF-8')}), 201)
# returns 403 if password is wrong
return make_response(
'Could not verify',
403,
{'WWW-Authenticate' : 'Basic realm ="Wrong Password !!"'}
)
# signup route
@app.route('/signup', methods =['POST'])
def signup():
# creates a dictionary of the form data
data = request.form
# gets name, email and password
name, email = data.get('name'), data.get('email')
password = data.get('password')
# checking for existing user
user = User.query\
.filter_by(email = email)\
.first()
if not user:
# database ORM object
user = User(
public_id = str(uuid.uuid4()),
name = name,
email = email,
password = generate_password_hash(password)
)
# insert user
db.session.add(user)
db.session.commit()
return make_response('Successfully registered.', 201)
else:
# returns 202 if user already exists
return make_response('User already exists. Please Log in.', 202)
if __name__ == "__main__":
# setting debug to True enables hot reload
# and also provides a debugger shell
# if you hit an error while running the server
app.run(debug = True)
现在,我们的代码已经准备好了。我们现在需要先创建数据库,然后从 ORM(对象关系映射)中创建表User 。为此,首先在终端中启动 python3 解释器。您可以通过在终端中键入python3来做到这一点,这应该可以为您解决问题。
接下来,您需要在 python3 解释器中键入以下内容:
from app import db
db.create_all()
因此,它首先导入数据库对象,然后调用create_all()函数从 ORM 创建所有表。它应该看起来像这样。
现在我们的实际代码已经准备好了,让我们测试一下。我建议使用邮递员来测试 API。您可以使用 CURL 之类的东西,但我将在本教程中使用邮递员。
要开始测试我们的 API,首先我们需要运行我们的 API。为此,请打开一个终端窗口并在其中键入以下内容。
python app.py
你应该看到这样的输出
如果您遇到任何错误,请确保您的所有语法和缩进都是正确的。可以看到我们的api运行在http://localhost:5000/上。复制此网址。我们将使用这个 urlalong 和路由来测试 api。
现在,打开Postman 。您应该对以下屏幕感到满意。
现在,单击+号并输入 url localhost:5000/signup将请求类型更改为POST ,然后选择Body ,然后选择form-data并将数据作为键值对输入,然后单击Send ,您应该会收到响应.它应该看起来像这样。
所以,我们注册了。现在让我们登录。为此,只需将端点更改为/login并取消选中Name字段并单击Send 。您应该得到一个 JWT 作为响应。记下 JWT。这将是我们的令牌,我们需要将该令牌与每个后续请求一起发送。此令牌将我们识别为已登录。
JSON 包含令牌。记下来。接下来尝试获取用户列表。为此,请将端点更改为/user ,然后在 headers 部分中,添加一个字段作为x-access-token并在值中添加 JWT 令牌,然后单击Send 。您将获得 JSON 格式的用户列表。
因此,这就是您在 Flask 中使用 JWT 执行身份验证的方式。我建议您更多地练习 JWT 和用户身份验证,以使您的概念更加清晰。