Comment implémenter l'authentification dans un système client-serveur Python

PythonBeginner
Pratiquer maintenant

Introduction

La mise en œuvre d'une authentification sécurisée est un aspect crucial de la construction de systèmes client-serveur Python robustes. Ce tutoriel vous guidera à travers le processus de mise en œuvre de l'authentification dans vos applications Python, en commençant par l'authentification de base par nom d'utilisateur et mot de passe, puis en passant aux méthodes basées sur des jetons (token-based). À la fin de ce lab, vous comprendrez comment sécuriser vos applications Python avec des mécanismes d'authentification appropriés.

Comprendre les bases de l'authentification et configurer votre environnement

L'authentification est le processus de vérification de l'identité d'un utilisateur ou d'un système avant d'accorder l'accès à des ressources protégées. Dans les systèmes client-serveur, une authentification appropriée garantit que seuls les utilisateurs autorisés peuvent accéder aux données sensibles ou effectuer certaines opérations.

Configuration de votre environnement

Commençons par configurer notre environnement de travail. Tout d'abord, créez un nouveau répertoire pour notre projet :

mkdir -p ~/project/auth_demo
cd ~/project/auth_demo

Ensuite, nous devons installer les paquets Python nécessaires que nous utiliserons tout au long de ce lab :

pip install flask flask-login requests

Vous devriez voir une sortie similaire à celle-ci :

Collecting flask
  Downloading Flask-2.2.3-py3-none-any.whl (101 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 101.8/101.8 KB 6.2 MB/s eta 0:00:00
Collecting flask-login
  Downloading Flask_Login-0.6.2-py3-none-any.whl (17 kB)
Collecting requests
  Downloading requests-2.29.0-py3-none-any.whl (62 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 62.5/62.5 KB 5.5 MB/s eta 0:00:00
...
Successfully installed flask-2.2.3 flask-login-0.6.2 requests-2.29.0 ...

Concepts d'authentification

Avant de plonger dans l'implémentation, comprenons les concepts de base de l'authentification :

  1. Authentification par nom d'utilisateur et mot de passe : La forme la plus courante où les utilisateurs fournissent des informations d'identification pour vérifier leur identité.

  2. Authentification basée sur des jetons (Token-based Authentication) : Après une connexion réussie, le serveur génère un jeton pour que le client l'utilise dans les requêtes suivantes, évitant ainsi d'avoir à envoyer des informations d'identification à chaque fois.

  3. Authentification basée sur une session (Session-based Authentication) : Le serveur stocke les informations de session après une connexion réussie et fournit un ID de session au client.

Visualisons un flux d'authentification de base :

Client                                Server
  |                                     |
  |--- Requête de connexion (informations d'identification) ---->|
  |                                     |--- Vérifier les informations d'identification
  |                                     |
  |<---- Réponse d'authentification -------|
  |                                     |
  |--- Requêtes suivantes (avec authentification) >|
  |                                     |--- Vérifier l'authentification
  |                                     |
  |<---- Réponse de la ressource protégée ---|

Maintenant que nous comprenons les bases et que nous avons configuré notre environnement, créons une structure de fichiers simple pour notre projet :

touch ~/project/auth_demo/server.py
touch ~/project/auth_demo/client.py

Dans l'étape suivante, nous allons implémenter un système d'authentification de base par nom d'utilisateur et mot de passe en utilisant Flask côté serveur et un client Python pour s'authentifier auprès de lui.

Implémentation de l'authentification de base par nom d'utilisateur et mot de passe

Dans cette étape, nous allons implémenter un système d'authentification de base qui utilise des noms d'utilisateur et des mots de passe. Nous allons créer :

  1. Un serveur Flask qui gère l'authentification
  2. Un client Python qui s'authentifie auprès du serveur

Création du serveur

Implémentons notre serveur Flask avec des capacités d'authentification de base. Ouvrez le fichier server.py dans l'éditeur VSCode :

code ~/project/auth_demo/server.py

Ajoutez maintenant le code suivant pour implémenter un serveur d'authentification simple :

from flask import Flask, request, jsonify, session
import os

app = Flask(__name__)
## Generate a random secret key for session management
app.secret_key = os.urandom(24)

## Simple in-memory user database
users = {
    "alice": "password123",
    "bob": "qwerty456"
}

@app.route('/login', methods=['POST'])
def login():
    data = request.get_json()

    ## Extract username and password from the request
    username = data.get('username')
    password = data.get('password')

    ## Check if the user exists and the password is correct
    if username in users and users[username] == password:
        session['logged_in'] = True
        session['username'] = username
        return jsonify({"status": "success", "message": f"Welcome {username}!"})
    else:
        return jsonify({"status": "error", "message": "Invalid credentials"}), 401

@app.route('/protected', methods=['GET'])
def protected():
    ## Check if the user is logged in
    if session.get('logged_in'):
        return jsonify({
            "status": "success",
            "message": f"You are viewing protected content, {session.get('username')}!"
        })
    else:
        return jsonify({"status": "error", "message": "Authentication required"}), 401

@app.route('/logout', methods=['POST'])
def logout():
    ## Remove session data
    session.pop('logged_in', None)
    session.pop('username', None)
    return jsonify({"status": "success", "message": "Logged out successfully"})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

Ce serveur :

  • Utilise un dictionnaire simple en mémoire pour stocker les noms d'utilisateur et les mots de passe
  • Fournit des points de terminaison (endpoints) pour la connexion, l'accès au contenu protégé et la déconnexion
  • Utilise la gestion de session de Flask pour maintenir l'état de l'authentification

Création du client

Maintenant, créons un client qui peut s'authentifier auprès de notre serveur. Ouvrez le fichier client.py :

code ~/project/auth_demo/client.py

Ajoutez le code suivant :

import requests
import json

## Server URL
BASE_URL = "http://localhost:5000"

def login(username, password):
    """Authenticate with the server using username and password"""
    response = requests.post(
        f"{BASE_URL}/login",
        json={"username": username, "password": password}
    )

    print(f"Login response: {response.status_code}")
    print(response.json())

    ## Return the session cookies if login was successful
    return response.cookies if response.status_code == 200 else None

def access_protected(cookies=None):
    """Access a protected resource"""
    response = requests.get(f"{BASE_URL}/protected", cookies=cookies)

    print(f"Protected resource response: {response.status_code}")
    print(response.json())

    return response

def logout(cookies=None):
    """Logout from the server"""
    response = requests.post(f"{BASE_URL}/logout", cookies=cookies)

    print(f"Logout response: {response.status_code}")
    print(response.json())

    return response

if __name__ == "__main__":
    ## Test with valid credentials
    print("=== Authenticating with valid credentials ===")
    cookies = login("alice", "password123")

    if cookies:
        print("\n=== Accessing protected resource ===")
        access_protected(cookies)

        print("\n=== Logging out ===")
        logout(cookies)

    ## Test with invalid credentials
    print("\n=== Authenticating with invalid credentials ===")
    login("alice", "wrongpassword")

    ## Try to access protected resource without authentication
    print("\n=== Accessing protected resource without authentication ===")
    access_protected()

Ce client :

  • Fournit des fonctions pour se connecter, accéder à une ressource protégée et se déconnecter
  • Gère les cookies pour maintenir la session entre les requêtes
  • Teste les informations d'identification valides et non valides

Exécution du système d'authentification

Pour voir notre système d'authentification en action, nous devons exécuter à la fois le serveur et le client. Tout d'abord, démarrons le serveur :

python ~/project/auth_demo/server.py

Vous devriez voir une sortie similaire à celle-ci :

 * Serving Flask app 'server'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5000

Maintenant, ouvrez un nouvel onglet de terminal en cliquant sur le bouton "+" à côté du terminal, et exécutez le client :

cd ~/project/auth_demo
python client.py

Vous devriez voir une sortie démontrant :

  1. Une connexion réussie avec des informations d'identification valides
  2. L'accès aux ressources protégées avec une authentification valide
  3. Une déconnexion réussie
  4. Une tentative de connexion ayant échoué avec des informations d'identification incorrectes
  5. Une tentative d'accès aux ressources protégées ayant échoué sans authentification

Cette sortie confirme que notre système d'authentification de base par nom d'utilisateur et mot de passe fonctionne correctement.

Dans l'étape suivante, nous améliorerons ce système en implémentant l'authentification basée sur des jetons (token-based) pour une sécurité améliorée.

Implémentation de l'authentification basée sur des jetons (Token-Based Authentication)

Dans l'étape précédente, nous avons créé un système d'authentification de base utilisant des sessions. Bien que cela fonctionne pour des applications simples, l'authentification basée sur des jetons offre plusieurs avantages, en particulier pour les API et les systèmes distribués.

Dans l'authentification basée sur des jetons :

  • Le serveur génère un jeton après une authentification réussie
  • Le client stocke et envoie ce jeton avec les requêtes suivantes
  • Le serveur valide le jeton au lieu de vérifier les informations d'identification à chaque fois

Améliorons notre système pour utiliser les JSON Web Tokens (JWT), une norme populaire pour l'authentification basée sur des jetons.

Installation des paquets requis

Tout d'abord, nous devons installer le paquet PyJWT :

pip install pyjwt

Vous devriez voir une sortie confirmant l'installation :

Collecting pyjwt
  Downloading PyJWT-2.6.0-py3-none-any.whl (20 kB)
Installing collected packages: pyjwt
Successfully installed pyjwt-2.6.0

Mise à jour du serveur pour l'authentification basée sur des jetons

Modifions notre serveur pour utiliser les jetons JWT au lieu des sessions. Mettez à jour le fichier server.py :

code ~/project/auth_demo/server.py

Remplacez le code existant par :

from flask import Flask, request, jsonify
import jwt
import datetime
import os

app = Flask(__name__)
## Secret key for signing JWT tokens
SECRET_KEY = os.urandom(24)

## Simple in-memory user database
users = {
    "alice": "password123",
    "bob": "qwerty456"
}

def generate_token(username):
    """Generate a JWT token for the authenticated user"""
    ## Token expires after 30 minutes
    expiration = datetime.datetime.utcnow() + datetime.timedelta(minutes=30)

    payload = {
        'username': username,
        'exp': expiration
    }

    ## Create the JWT token
    token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
    return token

def verify_token(token):
    """Verify the JWT token"""
    try:
        ## Decode and verify the token
        payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
        return payload
    except jwt.ExpiredSignatureError:
        ## Token has expired
        return None
    except jwt.InvalidTokenError:
        ## Invalid token
        return None

@app.route('/login', methods=['POST'])
def login():
    data = request.get_json()

    ## Extract username and password from the request
    username = data.get('username')
    password = data.get('password')

    ## Check if the user exists and the password is correct
    if username in users and users[username] == password:
        ## Generate a JWT token
        token = generate_token(username)

        return jsonify({
            "status": "success",
            "message": f"Welcome {username}!",
            "token": token
        })
    else:
        return jsonify({"status": "error", "message": "Invalid credentials"}), 401

@app.route('/protected', methods=['GET'])
def protected():
    ## Get the token from the Authorization header
    auth_header = request.headers.get('Authorization')

    if not auth_header or not auth_header.startswith('Bearer '):
        return jsonify({"status": "error", "message": "Missing or invalid token"}), 401

    ## Extract the token
    token = auth_header.split(' ')[1]

    ## Verify the token
    payload = verify_token(token)

    if payload:
        return jsonify({
            "status": "success",
            "message": f"You are viewing protected content, {payload['username']}!"
        })
    else:
        return jsonify({"status": "error", "message": "Invalid or expired token"}), 401

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

Ce serveur mis à jour :

  • Génère des jetons JWT lors d'une authentification réussie
  • Valide les jetons pour les ressources protégées
  • Possède des points de terminaison (endpoints) pour la connexion et l'accès au contenu protégé

Mise à jour du client pour l'authentification basée sur des jetons

Maintenant, mettons à jour notre client pour utiliser les jetons JWT. Mettez à jour le fichier client.py :

code ~/project/auth_demo/client.py

Remplacez le code existant par :

import requests
import json

## Server URL
BASE_URL = "http://localhost:5000"

def login(username, password):
    """Authenticate with the server using username and password"""
    response = requests.post(
        f"{BASE_URL}/login",
        json={"username": username, "password": password}
    )

    print(f"Login response: {response.status_code}")
    data = response.json()
    print(data)

    ## Return the token if login was successful
    return data.get('token') if response.status_code == 200 else None

def access_protected(token=None):
    """Access a protected resource using JWT token"""
    headers = {}
    if token:
        headers['Authorization'] = f'Bearer {token}'

    response = requests.get(f"{BASE_URL}/protected", headers=headers)

    print(f"Protected resource response: {response.status_code}")
    print(response.json())

    return response

if __name__ == "__main__":
    ## Test with valid credentials
    print("=== Authenticating with valid credentials ===")
    token = login("alice", "password123")

    if token:
        print("\n=== Accessing protected resource with valid token ===")
        access_protected(token)

    ## Test with invalid credentials
    print("\n=== Authenticating with invalid credentials ===")
    login("alice", "wrongpassword")

    ## Try to access protected resource without token
    print("\n=== Accessing protected resource without token ===")
    access_protected()

    ## Try to access protected resource with invalid token
    print("\n=== Accessing protected resource with invalid token ===")
    access_protected("invalid.token.value")

Ce client mis à jour :

  • Récupère et stocke le jeton JWT après une authentification réussie
  • Envoie le jeton dans l'en-tête Authorization pour accéder aux ressources protégées
  • Teste divers scénarios, y compris les tentatives d'authentification valides et non valides

Exécution du système d'authentification basé sur des jetons

Exécutons maintenant notre système d'authentification basé sur des jetons mis à jour. Tout d'abord, arrêtez tous les processus serveur en cours d'exécution avec Ctrl+C, puis démarrez le serveur mis à jour :

python ~/project/auth_demo/server.py

Ouvrez un nouvel onglet de terminal ou utilisez le deuxième onglet existant pour exécuter le client :

cd ~/project/auth_demo
python client.py

Vous devriez voir une sortie démontrant :

  1. Une connexion réussie avec des informations d'identification valides, recevant un jeton JWT
  2. L'accès aux ressources protégées avec un jeton valide
  3. Une tentative de connexion ayant échoué avec des informations d'identification incorrectes
  4. Une tentative d'accès aux ressources protégées ayant échoué sans jeton
  5. Une tentative d'accès aux ressources protégées ayant échoué avec un jeton non valide

Cette sortie confirme que notre système d'authentification basé sur des jetons fonctionne correctement.

Avantages de l'authentification basée sur des jetons

L'authentification basée sur des jetons offre plusieurs avantages par rapport à l'authentification basée sur des sessions :

  1. Sans état (Stateless) : Le serveur n'a pas besoin de stocker les informations de session.
  2. Évolutivité (Scalability) : Fonctionne bien dans les environnements distribués avec plusieurs serveurs.
  3. Adapté aux mobiles (Mobile-friendly) : Convient aux applications mobiles où les cookies pourraient ne pas fonctionner correctement.
  4. Multi-domaines (Cross-domain) : Peut être utilisé facilement sur différents domaines.
  5. Sécurité (Security) : Les jetons peuvent être configurés pour expirer, réduisant le risque de détournement de session.

Dans les applications du monde réel, vous souhaiterez peut-être améliorer davantage votre système d'authentification avec des fonctionnalités telles que l'actualisation des jetons, le contrôle d'accès basé sur les rôles et le stockage sécurisé des jetons.

Améliorer la sécurité avec le hachage des mots de passe

Dans nos implémentations précédentes, nous avons stocké les mots de passe en texte clair, ce qui constitue un risque de sécurité important. Dans cette étape, nous allons améliorer notre système d'authentification en implémentant le hachage des mots de passe à l'aide de l'algorithme bcrypt.

Pourquoi hacher les mots de passe ?

Stocker les mots de passe en texte clair présente plusieurs risques de sécurité :

  • Si votre base de données est compromise, les attaquants peuvent immédiatement utiliser les mots de passe
  • Les utilisateurs réutilisent souvent les mots de passe sur plusieurs sites, de sorte qu'une violation peut affecter d'autres services
  • Cela viole les meilleures pratiques de sécurité et peut enfreindre les réglementations en matière de protection des données

Le hachage des mots de passe résout ces problèmes en :

  • Convertissant les mots de passe en chaînes de longueur fixe qui ne peuvent pas être inversées
  • Ajoutant un "sel" pour empêcher les attaquants d'utiliser des tables de recherche précalculées (tables arc-en-ciel)
  • S'assurant que même si deux utilisateurs ont le même mot de passe, leurs hachages seront différents

Installation des paquets requis

Tout d'abord, nous devons installer le paquet bcrypt :

pip install bcrypt

Vous devriez voir une sortie confirmant l'installation :

Collecting bcrypt
  Downloading bcrypt-4.0.1-cp36-abi3-manylinux_2_28_x86_64.whl (593 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 593.8/593.8 KB 10.5 MB/s eta 0:00:00
Installing collected packages: bcrypt
Successfully installed bcrypt-4.0.1

Création d'un système d'enregistrement des utilisateurs

Améliorons notre serveur pour inclure l'enregistrement des utilisateurs avec le hachage des mots de passe. Mettez à jour le fichier server.py :

code ~/project/auth_demo/server.py

Remplacez le code existant par :

from flask import Flask, request, jsonify
import jwt
import datetime
import os
import bcrypt

app = Flask(__name__)
## Secret key for signing JWT tokens
SECRET_KEY = os.urandom(24)

## Store users as a dictionary with username as key and a dict of hashed password and roles as value
users = {}

def hash_password(password):
    """Hash a password using bcrypt"""
    ## Convert password to bytes if it's a string
    if isinstance(password, str):
        password = password.encode('utf-8')

    ## Generate a salt and hash the password
    salt = bcrypt.gensalt()
    hashed = bcrypt.hashpw(password, salt)

    return hashed

def check_password(password, hashed):
    """Verify a password against its hash"""
    ## Convert password to bytes if it's a string
    if isinstance(password, str):
        password = password.encode('utf-8')

    ## Check if the password matches the hash
    return bcrypt.checkpw(password, hashed)

def generate_token(username, role):
    """Generate a JWT token for the authenticated user"""
    ## Token expires after 30 minutes
    expiration = datetime.datetime.utcnow() + datetime.timedelta(minutes=30)

    payload = {
        'username': username,
        'role': role,
        'exp': expiration
    }

    ## Create the JWT token
    token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
    return token

def verify_token(token):
    """Verify the JWT token"""
    try:
        ## Decode and verify the token
        payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
        return payload
    except jwt.ExpiredSignatureError:
        ## Token has expired
        return None
    except jwt.InvalidTokenError:
        ## Invalid token
        return None

@app.route('/register', methods=['POST'])
def register():
    data = request.get_json()

    ## Extract registration data
    username = data.get('username')
    password = data.get('password')

    ## Basic validation
    if not username or not password:
        return jsonify({"status": "error", "message": "Username and password are required"}), 400

    ## Check if the username already exists
    if username in users:
        return jsonify({"status": "error", "message": "Username already exists"}), 409

    ## Hash the password and store the user
    hashed_password = hash_password(password)
    users[username] = {
        'password': hashed_password,
        'role': 'user'  ## Default role
    }

    return jsonify({
        "status": "success",
        "message": f"User {username} registered successfully"
    })

@app.route('/login', methods=['POST'])
def login():
    data = request.get_json()

    ## Extract username and password from the request
    username = data.get('username')
    password = data.get('password')

    ## Check if the user exists
    if username not in users:
        return jsonify({"status": "error", "message": "Invalid credentials"}), 401

    ## Check if the password is correct
    if check_password(password, users[username]['password']):
        ## Generate a JWT token
        token = generate_token(username, users[username]['role'])

        return jsonify({
            "status": "success",
            "message": f"Welcome {username}!",
            "token": token
        })
    else:
        return jsonify({"status": "error", "message": "Invalid credentials"}), 401

@app.route('/protected', methods=['GET'])
def protected():
    ## Get the token from the Authorization header
    auth_header = request.headers.get('Authorization')

    if not auth_header or not auth_header.startswith('Bearer '):
        return jsonify({"status": "error", "message": "Missing or invalid token"}), 401

    ## Extract the token
    token = auth_header.split(' ')[1]

    ## Verify the token
    payload = verify_token(token)

    if payload:
        return jsonify({
            "status": "success",
            "message": f"You are viewing protected content, {payload['username']}!",
            "role": payload['role']
        })
    else:
        return jsonify({"status": "error", "message": "Invalid or expired token"}), 401

@app.route('/admin', methods=['GET'])
def admin():
    ## Get the token from the Authorization header
    auth_header = request.headers.get('Authorization')

    if not auth_header or not auth_header.startswith('Bearer '):
        return jsonify({"status": "error", "message": "Missing or invalid token"}), 401

    ## Extract the token
    token = auth_header.split(' ')[1]

    ## Verify the token
    payload = verify_token(token)

    if not payload:
        return jsonify({"status": "error", "message": "Invalid or expired token"}), 401

    ## Check if user has admin role
    if payload['role'] != 'admin':
        return jsonify({"status": "error", "message": "Admin access required"}), 403

    return jsonify({
        "status": "success",
        "message": f"Welcome to the admin panel, {payload['username']}!"
    })

## Add an admin user for testing
admin_password = hash_password("admin123")
users["admin"] = {
    'password': admin_password,
    'role': 'admin'
}

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

Ce serveur amélioré :

  • Utilise bcrypt pour le hachage sécurisé des mots de passe
  • Inclut un point de terminaison (endpoint) d'enregistrement des utilisateurs
  • Ajoute un contrôle d'accès basé sur les rôles (administrateur par rapport aux utilisateurs réguliers)
  • Crée au préalable un utilisateur administrateur pour les tests

Mise à jour du client

Maintenant, mettons à jour notre client pour tester ces nouvelles fonctionnalités. Mettez à jour le fichier client.py :

code ~/project/auth_demo/client.py

Remplacez le code existant par :

import requests
import json

## Server URL
BASE_URL = "http://localhost:5000"

def register(username, password):
    """Register a new user"""
    response = requests.post(
        f"{BASE_URL}/register",
        json={"username": username, "password": password}
    )

    print(f"Registration response: {response.status_code}")
    print(response.json())

    return response.status_code == 200

def login(username, password):
    """Authenticate with the server using username and password"""
    response = requests.post(
        f"{BASE_URL}/login",
        json={"username": username, "password": password}
    )

    print(f"Login response: {response.status_code}")
    data = response.json()
    print(data)

    ## Return the token if login was successful
    return data.get('token') if response.status_code == 200 else None

def access_protected(token=None):
    """Access a protected resource using JWT token"""
    headers = {}
    if token:
        headers['Authorization'] = f'Bearer {token}'

    response = requests.get(f"{BASE_URL}/protected", headers=headers)

    print(f"Protected resource response: {response.status_code}")
    print(response.json())

    return response

def access_admin(token=None):
    """Access the admin panel using JWT token"""
    headers = {}
    if token:
        headers['Authorization'] = f'Bearer {token}'

    response = requests.get(f"{BASE_URL}/admin", headers=headers)

    print(f"Admin panel response: {response.status_code}")
    print(response.json())

    return response

if __name__ == "__main__":
    ## Test user registration
    print("=== Registering a new user ===")
    register("testuser", "testpass123")

    ## Try to register with the same username (should fail)
    print("\n=== Registering with existing username ===")
    register("testuser", "differentpass")

    ## Test regular user login and access
    print("\n=== Regular user login ===")
    user_token = login("testuser", "testpass123")

    if user_token:
        print("\n=== Regular user accessing protected resource ===")
        access_protected(user_token)

        print("\n=== Regular user trying to access admin panel ===")
        access_admin(user_token)

    ## Test admin login and access
    print("\n=== Admin login ===")
    admin_token = login("admin", "admin123")

    if admin_token:
        print("\n=== Admin accessing protected resource ===")
        access_protected(admin_token)

        print("\n=== Admin accessing admin panel ===")
        access_admin(admin_token)

Ce client mis à jour :

  • Teste la fonctionnalité d'enregistrement des utilisateurs
  • Démontre le contrôle d'accès basé sur les rôles
  • Teste à la fois l'authentification des utilisateurs réguliers et des administrateurs

Exécution du système d'authentification amélioré

Exécutons maintenant notre système d'authentification amélioré. Tout d'abord, arrêtez tous les processus serveur en cours d'exécution avec Ctrl+C, puis démarrez le serveur mis à jour :

python ~/project/auth_demo/server.py

Ouvrez un nouvel onglet de terminal ou utilisez le deuxième onglet existant pour exécuter le client :

cd ~/project/auth_demo
python client.py

Vous devriez voir une sortie démontrant :

  1. L'enregistrement réussi d'un utilisateur
  2. L'échec de l'enregistrement avec un nom d'utilisateur existant
  3. La connexion d'un utilisateur régulier et l'accès aux ressources protégées
  4. L'échec de la tentative d'un utilisateur régulier d'accéder au panneau d'administration
  5. La connexion de l'administrateur et l'accès aux ressources protégées et au panneau d'administration

Cette sortie confirme que notre système d'authentification amélioré avec le hachage des mots de passe et le contrôle d'accès basé sur les rôles fonctionne correctement.

Avantages de la sécurité

Notre système amélioré fournit désormais :

  1. Stockage sécurisé des mots de passe : Les mots de passe sont hachés à l'aide de bcrypt, une fonction de hachage lente conçue pour résister aux attaques par force brute.
  2. Contrôle d'accès basé sur les rôles : Différents utilisateurs peuvent avoir différents niveaux d'accès en fonction de leurs rôles.
  3. Authentification basée sur des jetons : Continue de fournir les avantages des jetons JWT que nous avons implémentés précédemment.
  4. Enregistrement des utilisateurs : Permet la création dynamique d'utilisateurs avec un stockage sécurisé des mots de passe.

Ces améliorations rendent notre système d'authentification beaucoup plus robuste et adapté aux applications du monde réel.

Résumé

Dans ce laboratoire, vous avez appris à implémenter l'authentification dans un système client-serveur Python, des concepts de base aux techniques avancées. Vous avez réussi à :

  1. Comprendre les concepts fondamentaux de l'authentification dans les systèmes client-serveur
  2. Implémenter l'authentification de base par nom d'utilisateur et mot de passe à l'aide des sessions Flask
  3. Améliorer la sécurité avec l'authentification basée sur des jetons (token-based authentication) en utilisant JWT
  4. Ajouter le hachage sécurisé des mots de passe avec bcrypt
  5. Implémenter le contrôle d'accès basé sur les rôles pour différents types d'utilisateurs

Ces compétences fournissent une base solide pour la création d'applications Python sécurisées. N'oubliez pas que l'authentification n'est qu'un aspect de la sécurité des applications - dans les systèmes de production, vous devez également envisager :

  • L'utilisation de HTTPS pour toutes les communications
  • L'implémentation de la limitation du débit (rate limiting) pour empêcher les attaques par force brute
  • L'ajout de la journalisation et de la surveillance des activités suspectes
  • La mise à jour de toutes les dépendances pour corriger les vulnérabilités de sécurité

En appliquant ces principes, vous pouvez créer des applications Python qui protègent efficacement les données des utilisateurs et maintiennent des opérations sécurisées.