📌  相关文章
📜  在 Flask 中使用 JWT 进行用户身份验证

📅  最后修改于: 2022-05-13 01:55:11.438000             🧑  作者: Mango

在 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

你应该看到这样的输出

Python 应用程序.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 和用户身份验证,以使您的概念更加清晰。