Apprenons à sécuriser une API REST avec des jetons Web JSON pour empêcher les utilisateurs et les applications tierces d'en abuser.
Nous allons construire un service de base de données en utilisant SQLite et permettre aux utilisateurs d'y accéder via une API REST à l'aide de méthodes HTTP telles que POST et PUT.
De plus, nous découvrirons pourquoi les jetons Web JSON sont un moyen approprié de protéger l'API de repos au lieu de l'authentification Digest et de base. Avant de continuer, comprenons le terme jetons Web JSON, API REST et framework Flask.
Jetons Web JSON
Jeton Web JSON, également appelé JWT, est le moyen sécurisé de transférer des jetons aléatoires entre deux parties ou entités. JSON est généralement composé de trois parties comme suit.
- Charge utile
- En-tête
- Signature
JSON utilise deux types de formulaires de structure lors du transfert de données ou d'informations entre deux parties.
- Sérialisé
- Désérialisé
Le formulaire sérialisé est utilisé lors du transfert de données vers le réseau via chaque demande et réponse, tandis que le formulaire désérialisé est utilisé lors de la lecture et de l'écriture de données sur le jeton Web.
Dans la forme sérialisée, il y a trois composants.
- En-tête
- Charge utile
- Signature
Le composant d'en-tête définit les informations cryptographiques sur les jetons. Par exemple:
- Est-il signé ou non signé JWT?
- Définir les techniques d'algorithme
Le formulaire désérialisé, contrairement au formulaire sérialisé, contient deux composants.
- Charge utile
- En-tête
API REST
L'API (interface de programmation d'application) permet la communication entre deux applications pour récupérer ou soumettre les données. Il existe deux types d'API courants: les API Web et système.
Dans cet article, nous examinerons uniquement l'API Web. Il existe deux types d'API Web.
- Request - API de réponse: repos, GraphQL, appel de procédure à distance (RPC)
- API événementielle: WebHooks, Web Sockets, streaming HTTP
L'API REST appartient à la catégorie requête-réponse. Il utilise des méthodes HTTP telles que GET, POST et PUT pour effectuer des opérations API.
Un exemple classique est lorsqu'un utilisateur envoie une méthode GET au service Web pour demander ou récupérer une ressource spécifique ou une collection de ressources. Le serveur renvoie ensuite la ressource spécifique ou la collection de ressources à l'utilisateur qui l'a demandée.
Cadre de flacon
Flask est un framework basé sur python. Il s'agit d'un micro-framework utilisé par les développeurs python pour créer une API de repos. On l'appelle un micro-framework car il permet aux développeurs, par exemple, d'ajouter une authentification personnalisée et tout autre système backend basé sur les préférences.
Commençons par l'implémentation. La configuration de mon système est la suivante.
- Ubuntu comme système d'exploitation
- Python 2.7 +
- Facteur
Configurer un environnement virtuel à l'aide de virtualenv
Nous devons mettre en place un environnement virtuel pour garantir que certains packages n'entreront pas en conflit avec les packages système. Utilisons le virtualenv
pour mettre en place un nouvel environnement virtuel.
En supposant que vous ayez le pip
commande disponible sur votre système, exécutez la commande suivante via pip
à installer.
pip install virtualenv
Si vous n'avez pas de pip sur votre machine, suivez ceci Documentation pour installer pip sur votre système.
Ensuite, créons un répertoire pour stocker ou contenir notre environnement virtuel. Utilisez le mkdir
commande ci-dessous pour créer un répertoire
mkdir flaskproject
Changer en flaskproject
répertoire à l'aide de la commande suivante
cd flaskproject
À l'intérieur de l' flaskproject
répertoire, utilisez le virtualenv
outil pour créer un environnement virtuel comme indiqué ci-dessous:
virtualenv flaskapi
Après avoir utilisé le virtualenv
outil pour créer l'environnement virtuel, exécutez le cd
commande pour changer en flaskapi
répertoire comme environnement virtuel et activez-le à l'aide de la commande ci-dessous.
source bin/activate
Exécutez toutes les tâches liées à ce projet dans l'environnement virtuel.
Installer des packages à l'aide de pip
Il est maintenant temps d'installer des packages tels que le framework flask et PyJWT que nous utiliserons pour créer le reste de l'API et d'autres packages nécessaires pour notre projet d'API.
Créer un requirements.txt
fichier avec les packages suivants.
Flask
datetime
uuid
Flask-SQLAlchemy
PyJWT
Installez-les avec pip.
pip install -r requirements.txt
Configurer une base de données
Installons SQLite.
apt-get install sqlite3
Créez une base de données nommée la bibliothèque. Dans cette base de données, nous allons créer deux tables, à savoir le Users
et Authors
tableau.
La table des utilisateurs contiendra les utilisateurs enregistrés. Seuls les utilisateurs enregistrés peuvent avoir accès à la table Auteurs.
La table des auteurs stockera les informations sur les auteurs ou les détails tels que le nom de l'auteur, le pays de naissance, etc. soumis par les utilisateurs enregistrés.
Créez la base de données à l'aide de la commande suivante:
sqlite3 library.db
Vous pouvez vérifier si vous avez créé la base de données avec succès en utilisant la commande ci-dessous:
.databases
Ouvrez un nouveau terminal et exécutez ce qui suit dans l'environnement virtuel que nous avons créé précédemment.
touch app.py
Collez le code suivant dans le fichier nommé app.py
from flask import Flask, request, jsonify, make_response
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
import uuid
import jwt
import datetime
from functools import wraps
La première ligne du code ci-dessus importe des packages tels que request
et jsonify
. Nous utiliserons request
pour garder une trace des données au niveau de la demande lors d'une demande et d'une utilisation jsonify
pour générer des réponses dans un JSON le format.
Sur la ligne suivante, nous avons importé SQLAlchemy de flask_sqlalchemy
afin d'intégrer les fonctionnalités de SQLAlchemy dans le flask.
À partir werkzeug.security
, nous avons importé generate_password_hash
pour générer un hachage de mot de passe pour les utilisateurs et check_password_hash
pour vérifier le mot de passe de l'utilisateur lors de la comparaison du mot de passe soumis par les utilisateurs avec les mots de passe des utilisateurs stockés dans la base de données.
Enfin, nous avons importé uuid
également connu sous le nom d'identificateurs uniques universels pour générer des numéros d'identification aléatoires pour les utilisateurs.
Pourtant, à l'intérieur du app.py
, implémentez les paramètres de configuration de l'API de la bibliothèque à l'aide du code ci-dessous dans le fichier app.py.
Placez le code suivant sous l'instruction d'importation.
app = Flask(__name__)
app.config['SECRET_KEY']='Th1s1ss3cr3t'
app.config['SQLALCHEMY_DATABASE_URI']='sqlite://///home/michael/geekdemos/geekapp/library.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
Créez maintenant deux modèles pour le tableau Utilisateurs et auteurs comme indiqué ci-dessous. Copiez et collez le code dans le fichier app.py.
Placez le code ci-dessous juste en dessous de ce paramètre de base de données db = SQLAlchemy(app)
class Users(db.Model):
id = db.Column(db.Integer, primary_key=True)
public_id = db.Column(db.Integer)
name = db.Column(db.String(50))
password = db.Column(db.String(50))
admin = db.Column(db.Boolean)
class Authors(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), unique=True, nullable=False))
book = db.Column(db.String(20), unique=True, nullable=False))
country = db.Column(db.String(50), nullable=False))
booker_prize = db.Column(db.Boolean)
Générer des tableaux d'utilisateurs et d'auteurs
Sur le terminal, tapez le code suivant dans l'environnement virtuel pour générer ou créer des tables pour les tables Utilisateurs et Auteurs comme indiqué ci-dessous
from app import db
db.create_all()
Ensuite, ouvrez le app.py
fichier dans l'environnement virtuel et créez une autre fonction.
Cette fonction générera des jetons afin de permettre uniquement aux utilisateurs enregistrés d'accéder et d'exécuter un ensemble d'opérations API sur la table Authors.
Placez ce code sous le modèle de base de données pour la table Auteurs
def token_required(f):
@wraps(f)
def decorator(*args, **kwargs):
token = None
if 'x-access-tokens' in request.headers:
token = request.headers['x-access-tokens']
if not token:
return jsonify({'message': 'a valid token is missing'})
try:
data = jwt.decode(token, app.config[SECRET_KEY])
current_user = Users.query.filter_by(public_id=data['public_id']).first()
except:
return jsonify({'message': 'token is invalid'})
return f(current_user, *args, **kwargs)
return decorator
Créer des itinéraires pour la table des utilisateurs
Créons maintenant une route pour permettre aux utilisateurs de s'inscrire à l'API Authors via un nom d'utilisateur et un mot de passe, comme indiqué ci-dessous.
Ouvrez à nouveau le app.py
fichier dans l'environnement virtuel et collez le code suivant sous la fonction token_required(f)
@app.route('/register', methods=['GET', 'POST'])
def signup_user():
data = request.get_json()
hashed_password = generate_password_hash(data['password'], method='sha256')
new_user = Users(public_id=str(uuid.uuid4()), name=data['name'], password=hashed_password, admin=False)
db.session.add(new_user)
db.session.commit()
return jsonify({'message': 'registered successfully'})
Dans l'environnement virtuel, créez un autre itinéraire dans le app.py
fichier pour permettre aux utilisateurs enregistrés de se connecter.
Lorsqu'un utilisateur se connecte, un jeton aléatoire est généré pour que l'utilisateur puisse accéder à l'API de la bibliothèque.
Collez le code ci-dessous sous l'itinéraire précédent que nous avons créé.
@app.route('/login', methods=['GET', 'POST'])
def login_user():
auth = request.authorization
if not auth or not auth.username or not auth.password:
return make_response('could not verify', 401, {'WWW.Authentication': 'Basic realm: "login required"'})
user = Users.query.filter_by(name=auth.username).first()
if check_password_hash(user.password, auth.password):
token = jwt.encode({'public_id': user.public_id, 'exp' : datetime.datetime.utcnow() + datetime.timedelta(minutes=30)}, app.config['SECRET_KEY'])
return jsonify({'token' : token.decode('UTF-8')})
return make_response('could not verify', 401, {'WWW.Authentication': 'Basic realm: "login required"'})
Pourtant, dans l'environnement virtuel, créez une autre route dans le app.py
fichier pour obtenir ou récupérer tous les utilisateurs enregistrés.
Ce code vérifie tous les utilisateurs enregistrés dans la table Users et renvoie le résultat final au format JSON.
Collez le code ci-dessous sous la route de connexion
@app.route('/users', methods=['GET'])
def get_all_users():
users = Users.query.all()
result = []
for user in users:
user_data = {}
user_data['public_id'] = user.public_id
user_data['name'] = user.name
user_data['password'] = user.password
user_data['admin'] = user.admin
result.append(user_data)
return jsonify({'users': result})
Créer des itinéraires pour la table des auteurs
Créons des routes pour la table Authors afin de permettre aux utilisateurs de récupérer tous les auteurs de la base de données, ainsi que de supprimer les auteurs.
Seuls les utilisateurs disposant de jetons valides peuvent effectuer ces opérations d'API.
Dans le fichier app.py, créez une route permettant aux utilisateurs enregistrés de créer de nouveaux auteurs.
Collez ce code sous l'itinéraire qui permet à un utilisateur de récupérer tous les utilisateurs enregistrés.
@app.route('/author', methods=['POST', 'GET'])
@token_required
def create_author(current_user):
data = request.get_json()
new_authors = Authors(name=data['name'], country=data['country'], book=data['book'], booker_prize=True, user_id=current_user.id)
db.session.add(new_authors)
db.session.commit()
return jsonify({'message' : 'new author created'})
Ensuite, créez une autre route pour permettre à un utilisateur enregistré avec un jeton valide de récupérer tous les auteurs dans le tableau Auteurs comme indiqué ci-dessous.
Collez ce code sous l'itinéraire qui permet à un utilisateur de créer un nouvel auteur.
@app.route('/authors', methods=['POST', 'GET']) @token_required def get_authors(current_user): authors = Authors.query.filter_by(user_id=current_user.id).all() output = [] for author in authors: author_data = {} author_data['name'] = author.name author_data['book'] = author.book author_data['country'] = author.country author_data['booker_prize'] = author.booker_prize output.append(author_data) return jsonify({'list_of_authors' : output})
Enfin, toujours à l'intérieur du app.py
fichier, créez une route pour supprimer un auteur spécifié comme indiqué ci-dessous.
Collez ce code sous l'itinéraire qui permet à un utilisateur de récupérer une liste d'auteurs.
@app.route('/authors/<author_id>', methods=['DELETE'])
@token_required
def delete_author(current_user, author_id):
author = Author.query.filter_by(id=author_id, user_id=current_user.id).first()
if not author:
return jsonify({'message': 'author does not exist'})
db.session.delete(author)
db.session.commit()
return jsonify({'message': 'Author deleted'})
if __name__ == '__main__':
app.run(debug=True)
Ensuite, enregistrez et fermez le fichier app.py dans l'environnement virtuel.
Testing the library API with Postman
Dans cette section, nous utiliserons un outil de facteur pour envoyer une demande aux services de base de données. Si vous n'avez pas de facteur sur votre machine, vous pouvez découvrir comment le télécharger et l'installer ici.
Outre le facteur, nous pouvons utiliser d'autres outils tels que Friser pour envoyer des requêtes au serveur.
Ouvrez un nouveau terminal et saisissez ce qui suit:
postman
La commande postman
entraînera l'affichage de la page ci-dessous par votre navigateur Web:
Vous pouvez décider de vous inscrire et de créer un compte gratuit, mais nous ignorerons et aurons un accès direct à l'application pour tester l'API de la bibliothèque comme indiqué ci-dessous:
Dans cette section, nous autoriserons un utilisateur à s'inscrire à l'API de la bibliothèque en fournissant un nom d'utilisateur et un mot de passe unique au format JSON en utilisant la méthode POST en suivant les étapes ci-dessous:
- Cliquez sur l'onglet intitulé Body
- Sélectionnez ensuite le bouton brut et choisissez le format JSON
- entrez un nom d'utilisateur et un mot de passe pour vous inscrire comme indiqué sur la capture d'écran
- À côté du bouton d'envoi, insérez l'URL suivante http://127.0.0.1/register
- Enfin, changez la méthode en POST et appuyez sur le bouton d'envoi.
Il affichera la sortie suivante comme indiqué ci-dessous:
Nous avons maintenant enregistré un utilisateur avec succès. Allons-y pour permettre à l'utilisateur qui vient de s'inscrire de se connecter afin de générer un jeton aléatoire temporaire pour accéder à la table Auteurs en utilisant les étapes suivantes:
- Cliquez sur l'onglet d'autorisation.
- Dans la section type, sélectionnez l'authentification de base.
- Remplissez ensuite le formulaire de nom d'utilisateur et de mot de passe avec le nom d'utilisateur et le mot de passe avec lesquels vous vous êtes enregistré précédemment.
- Enfin, appuyez sur le bouton d'envoi pour vous connecter et générer un jeton aléatoire.
Une fois que l'utilisateur s'est connecté avec succès, un jeton aléatoire est généré pour l'utilisateur, comme indiqué dans la capture d'écran.
Nous utiliserons le jeton aléatoire généré pour accéder à la table des auteurs.
Dans cette section, nous ajouterons les informations d'un auteur à la table Authors via la méthode POST en suivant les étapes suivantes:
- Cliquez sur l'onglet des en-têtes
- Incluez les en-têtes HTTP suivants affichés dans la capture d'écran
- Ensuite, cliquez sur l'onglet corps et entrez les détails du nouvel auteur
- Appuyez ensuite sur le bouton d'envoi pour ajouter les détails de l'auteur au tableau de l'auteur
Vous pouvez également récupérer les informations sur les auteurs dans le tableau Auteurs via les éléments suivants:
- Assurez-vous que votre jeton généré se trouve dans la section des en-têtes. s'il n'y est pas, vous devez le remplir avec votre token.
- À côté du bouton d'envoi, saisissez cette URL
http://127.0.0.1/authors
- Changez ensuite la méthode HTTP en GET et appuyez sur le bouton d'envoi pour récupérer les détails des auteurs.
Enfin, vous pouvez supprimer le (s) auteur (s) dans le tableau Auteurs via le DELETE
méthode en utilisant les étapes suivantes:
- Assurez-vous que votre jeton est toujours dans la section des en-têtes. Vous pouvez vérifier l'onglet des en-têtes pour vous assurer que les informations nécessaires sont en place.
- À côté du bouton d'envoi, saisissez cette URL
http://127.0.0.1/sam
- Appuyez ensuite sur le bouton d'envoi pour supprimer l'utilisateur que vous avez spécifié.
Vous pouvez trouver le code source complet sur Github. Vous pouvez le cloner et le vérifier sur votre machine.