So implementieren Sie die Authentifizierung in einem Python-Client-Server-System

PythonBeginner
Jetzt üben

Einführung

Die Implementierung einer sicheren Authentifizierung ist ein entscheidender Aspekt beim Aufbau robuster Python-Client-Server-Systeme. Dieses Tutorial führt Sie durch den Prozess der Implementierung von Authentifizierung in Ihren Python-Anwendungen, beginnend mit der grundlegenden Benutzername- und Passwort-Authentifizierung und übergehend zu tokenbasierten Methoden. Am Ende dieses Labors werden Sie verstehen, wie Sie Ihre Python-Anwendungen mit geeigneten Authentifizierungsmechanismen sichern können.

Grundlagen der Authentifizierung verstehen und Ihre Umgebung einrichten

Authentifizierung ist der Prozess der Überprüfung der Identität eines Benutzers oder Systems, bevor der Zugriff auf geschützte Ressourcen gewährt wird. In Client-Server-Systemen stellt eine ordnungsgemäße Authentifizierung sicher, dass nur autorisierte Benutzer auf sensible Daten zugreifen oder bestimmte Operationen ausführen können.

Einrichten Ihrer Umgebung

Beginnen wir mit der Einrichtung unserer Arbeitsumgebung. Erstellen Sie zunächst ein neues Verzeichnis für unser Projekt:

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

Als Nächstes müssen wir die notwendigen Python-Pakete installieren, die wir in diesem Labor verwenden werden:

pip install flask flask-login requests

Sie sollten eine Ausgabe ähnlich dieser sehen:

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 ...

Authentifizierungskonzepte

Bevor wir uns der Implementierung zuwenden, wollen wir die grundlegenden Authentifizierungskonzepte verstehen:

  1. Benutzername- und Passwort-Authentifizierung: Die gebräuchlichste Form, bei der Benutzer Anmeldeinformationen zur Überprüfung ihrer Identität angeben.

  2. Tokenbasierte Authentifizierung: Nach erfolgreicher Anmeldung generiert der Server ein Token für den Client, das in nachfolgenden Anfragen verwendet werden kann, wodurch die Notwendigkeit entfällt, jedes Mal Anmeldeinformationen zu senden.

  3. Sitzungsbasierte Authentifizierung: Der Server speichert Sitzungsinformationen nach einer erfolgreichen Anmeldung und stellt dem Client eine Sitzungs-ID zur Verfügung.

Lassen Sie uns einen grundlegenden Authentifizierungsablauf visualisieren:

Client                                Server
  |                                     |
  |--- Login Request (credentials) ---->|
  |                                     |--- Verify Credentials
  |                                     |
  |<---- Authentication Response -------|
  |                                     |
  |--- Subsequent Requests (with auth) >|
  |                                     |--- Verify Auth
  |                                     |
  |<---- Protected Resource Response ---|

Nachdem wir nun die Grundlagen verstanden und unsere Umgebung eingerichtet haben, erstellen wir eine einfache Dateistruktur für unser Projekt:

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

Im nächsten Schritt implementieren wir ein einfaches Benutzername- und Passwort-Authentifizierungssystem mit Flask auf der Serverseite und einem Python-Client, um sich damit zu authentifizieren.

Implementierung der grundlegenden Benutzername- und Passwort-Authentifizierung

In diesem Schritt implementieren wir ein grundlegendes Authentifizierungssystem, das Benutzernamen und Passwörter verwendet. Wir erstellen:

  1. Einen Flask-Server, der die Authentifizierung handhabt
  2. Einen Python-Client, der sich beim Server authentifiziert

Erstellen des Servers

Implementieren wir unseren Flask-Server mit grundlegenden Authentifizierungsfunktionen. Öffnen Sie die Datei server.py im VSCode-Editor:

code ~/project/auth_demo/server.py

Fügen Sie nun den folgenden Code hinzu, um einen einfachen Authentifizierungsserver zu implementieren:

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)

Dieser Server:

  • Verwendet ein einfaches In-Memory-Wörterbuch, um Benutzernamen und Passwörter zu speichern
  • Stellt Endpunkte für Login, den Zugriff auf geschützte Inhalte und Logout bereit
  • Verwendet Flasks Sitzungsverwaltung zur Aufrechterhaltung des Authentifizierungsstatus

Erstellen des Clients

Erstellen wir nun einen Client, der sich bei unserem Server authentifizieren kann. Öffnen Sie die Datei client.py:

code ~/project/auth_demo/client.py

Fügen Sie den folgenden Code hinzu:

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()

Dieser Client:

  • Stellt Funktionen zum Anmelden, zum Zugriff auf eine geschützte Ressource und zum Abmelden bereit
  • Verarbeitet Cookies, um die Sitzung zwischen Anfragen aufrechtzuerhalten
  • Testet sowohl gültige als auch ungültige Anmeldeinformationen

Ausführen des Authentifizierungssystems

Um unser Authentifizierungssystem in Aktion zu sehen, müssen wir sowohl den Server als auch den Client ausführen. Starten wir zuerst den Server:

python ~/project/auth_demo/server.py

Sie sollten eine Ausgabe ähnlich dieser sehen:

 * 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

Öffnen Sie nun einen neuen Terminal-Tab, indem Sie auf die Schaltfläche "+" neben dem Terminal klicken, und führen Sie den Client aus:

cd ~/project/auth_demo
python client.py

Sie sollten eine Ausgabe sehen, die Folgendes demonstriert:

  1. Erfolgreiche Anmeldung mit gültigen Anmeldeinformationen
  2. Zugriff auf geschützte Ressourcen mit gültiger Authentifizierung
  3. Erfolgreiche Abmeldung
  4. Fehlgeschlagener Anmeldeversuch mit falschen Anmeldeinformationen
  5. Fehlgeschlagener Versuch, auf geschützte Ressourcen ohne Authentifizierung zuzugreifen

Diese Ausgabe bestätigt, dass unser grundlegendes Benutzername- und Passwort-Authentifizierungssystem korrekt funktioniert.

Im nächsten Schritt werden wir dieses System erweitern, indem wir eine tokenbasierte Authentifizierung implementieren, um die Sicherheit zu verbessern.

Implementierung der tokenbasierten Authentifizierung

Im vorherigen Schritt haben wir ein grundlegendes Authentifizierungssystem unter Verwendung von Sitzungen erstellt. Während dies für einfache Anwendungen funktioniert, bietet die tokenbasierte Authentifizierung mehrere Vorteile, insbesondere für APIs und verteilte Systeme.

Bei der tokenbasierten Authentifizierung:

  • Der Server generiert nach erfolgreicher Authentifizierung ein Token
  • Der Client speichert und sendet dieses Token mit nachfolgenden Anfragen
  • Der Server validiert das Token, anstatt jedes Mal Anmeldeinformationen zu überprüfen

Lassen Sie uns unser System auf JSON Web Tokens (JWT) umstellen, einem beliebten Standard für die tokenbasierte Authentifizierung.

Installieren der erforderlichen Pakete

Zuerst müssen wir das PyJWT-Paket installieren:

pip install pyjwt

Sie sollten eine Ausgabe sehen, die die Installation bestätigt:

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

Aktualisieren des Servers für die tokenbasierte Authentifizierung

Ändern wir unseren Server so, dass er JWT-Tokens anstelle von Sitzungen verwendet. Aktualisieren Sie die Datei server.py:

code ~/project/auth_demo/server.py

Ersetzen Sie den vorhandenen Code durch:

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)

Dieser aktualisierte Server:

  • Generiert JWT-Tokens nach erfolgreicher Authentifizierung
  • Validiert Tokens für geschützte Ressourcen
  • Verfügt über Endpunkte für Login und den Zugriff auf geschützte Inhalte

Aktualisieren des Clients für die tokenbasierte Authentifizierung

Aktualisieren wir nun unseren Client, um die JWT-Tokens zu verwenden. Aktualisieren Sie die Datei client.py:

code ~/project/auth_demo/client.py

Ersetzen Sie den vorhandenen Code durch:

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")

Dieser aktualisierte Client:

  • Ruft das JWT-Token nach erfolgreicher Authentifizierung ab und speichert es
  • Sendet das Token im Authorization-Header für den Zugriff auf geschützte Ressourcen
  • Testet verschiedene Szenarien, einschließlich gültiger und ungültiger Authentifizierungsversuche

Ausführen des tokenbasierten Authentifizierungssystems

Führen wir nun unser aktualisiertes tokenbasiertes Authentifizierungssystem aus. Beenden Sie zuerst alle laufenden Serverprozesse mit Ctrl+C und starten Sie dann den aktualisierten Server:

python ~/project/auth_demo/server.py

Öffnen Sie einen neuen Terminal-Tab oder verwenden Sie den vorhandenen zweiten Tab, um den Client auszuführen:

cd ~/project/auth_demo
python client.py

Sie sollten eine Ausgabe sehen, die Folgendes demonstriert:

  1. Erfolgreiche Anmeldung mit gültigen Anmeldeinformationen, Erhalt eines JWT-Tokens
  2. Zugriff auf geschützte Ressourcen mit einem gültigen Token
  3. Fehlgeschlagener Anmeldeversuch mit falschen Anmeldeinformationen
  4. Fehlgeschlagener Versuch, auf geschützte Ressourcen ohne Token zuzugreifen
  5. Fehlgeschlagener Versuch, auf geschützte Ressourcen mit einem ungültigen Token zuzugreifen

Diese Ausgabe bestätigt, dass unser tokenbasiertes Authentifizierungssystem korrekt funktioniert.

Vorteile der tokenbasierten Authentifizierung

Die tokenbasierte Authentifizierung bietet gegenüber der sitzungsbasierten Authentifizierung mehrere Vorteile:

  1. Zustandslos (Stateless): Der Server muss keine Sitzungsinformationen speichern.
  2. Skalierbarkeit: Funktioniert gut in verteilten Umgebungen mit mehreren Servern.
  3. Mobilfreundlich: Geeignet für mobile Anwendungen, bei denen Cookies möglicherweise nicht gut funktionieren.
  4. Domänenübergreifend (Cross-domain): Kann problemlos über verschiedene Domänen hinweg verwendet werden.
  5. Sicherheit: Tokens können so konfiguriert werden, dass sie ablaufen, wodurch das Risiko von Sitzungsübernahmen verringert wird.

In realen Anwendungen möchten Sie Ihr Authentifizierungssystem möglicherweise mit Funktionen wie Token-Aktualisierung, rollenbasierter Zugriffskontrolle und sicherer Tokenspeicherung weiter verbessern.

Erhöhung der Sicherheit mit Passwort-Hashing

In unseren vorherigen Implementierungen haben wir Passwörter im Klartext gespeichert, was ein erhebliches Sicherheitsrisiko darstellt. In diesem Schritt verbessern wir unser Authentifizierungssystem, indem wir das Passwort-Hashing mit dem bcrypt-Algorithmus implementieren.

Warum Passwörter hashen?

Das Speichern von Passwörtern im Klartext birgt mehrere Sicherheitsrisiken:

  • Wenn Ihre Datenbank kompromittiert wird, können Angreifer die Passwörter sofort verwenden
  • Benutzer verwenden oft Passwörter auf mehreren Websites wieder, sodass eine Verletzung andere Dienste beeinträchtigen kann
  • Es verstößt gegen bewährte Sicherheitspraktiken und kann gegen Datenschutzbestimmungen verstoßen

Das Hashing von Passwörtern löst diese Probleme durch:

  • Umwandlung von Passwörtern in Zeichenfolgen fester Länge, die nicht umkehrbar sind
  • Hinzufügen eines "Salz" (Salt), um zu verhindern, dass Angreifer vorgefertigte Nachschlagetabellen (Rainbow Tables) verwenden
  • Sicherstellen, dass selbst wenn zwei Benutzer dasselbe Passwort haben, ihre Hashes unterschiedlich sind

Installieren der erforderlichen Pakete

Zuerst müssen wir das bcrypt-Paket installieren:

pip install bcrypt

Sie sollten eine Ausgabe sehen, die die Installation bestätigt:

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

Erstellen eines Benutzerregistrierungssystems

Erweitern wir unseren Server, um die Benutzerregistrierung mit Passwort-Hashing einzubeziehen. Aktualisieren Sie die Datei server.py:

code ~/project/auth_demo/server.py

Ersetzen Sie den vorhandenen Code durch:

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)

Dieser erweiterte Server:

  • Verwendet bcrypt für sicheres Passwort-Hashing
  • Enthält einen Benutzerregistrierungs-Endpunkt
  • Fügt rollenbasierte Zugriffskontrolle hinzu (Administrator vs. reguläre Benutzer)
  • Erstellt einen Administratorbenutzer für Tests vorab

Aktualisieren des Clients

Aktualisieren wir nun unseren Client, um diese neuen Funktionen zu testen. Aktualisieren Sie die Datei client.py:

code ~/project/auth_demo/client.py

Ersetzen Sie den vorhandenen Code durch:

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)

Dieser aktualisierte Client:

  • Testet die Benutzerregistrierungsfunktionalität
  • Demonstriert die rollenbasierte Zugriffskontrolle
  • Testet sowohl reguläre Benutzer- als auch Administratorauthentifizierung

Ausführen des erweiterten Authentifizierungssystems

Führen wir nun unser erweitertes Authentifizierungssystem aus. Beenden Sie zuerst alle laufenden Serverprozesse mit Ctrl+C und starten Sie dann den aktualisierten Server:

python ~/project/auth_demo/server.py

Öffnen Sie einen neuen Terminal-Tab oder verwenden Sie den vorhandenen zweiten Tab, um den Client auszuführen:

cd ~/project/auth_demo
python client.py

Sie sollten eine Ausgabe sehen, die Folgendes demonstriert:

  1. Erfolgreiche Benutzerregistrierung
  2. Fehlgeschlagene Registrierung mit einem vorhandenen Benutzernamen
  3. Reguläre Benutzeranmeldung und Zugriff auf geschützte Ressourcen
  4. Fehlgeschlagener Versuch eines regulären Benutzers, auf das Admin-Panel zuzugreifen
  5. Administratoranmeldung und Zugriff auf geschützte Ressourcen und das Admin-Panel

Diese Ausgabe bestätigt, dass unser erweitertes Authentifizierungssystem mit Passwort-Hashing und rollenbasierter Zugriffskontrolle korrekt funktioniert.

Sicherheitsvorteile

Unser erweitertes System bietet jetzt:

  1. Sichere Passwortspeicherung: Passwörter werden mit bcrypt gehasht, einer langsamen Hashing-Funktion, die darauf ausgelegt ist, Brute-Force-Angriffen zu widerstehen.
  2. Rollenbasierte Zugriffskontrolle: Verschiedene Benutzer können je nach ihren Rollen unterschiedliche Zugriffsebenen haben.
  3. Tokenbasierte Authentifizierung: Bietet weiterhin die Vorteile von JWT-Tokens, die wir zuvor implementiert haben.
  4. Benutzerregistrierung: Ermöglicht die dynamische Benutzererstellung mit sicherer Passwortspeicherung.

Diese Erweiterungen machen unser Authentifizierungssystem viel robuster und für reale Anwendungen geeignet.

Zusammenfassung

In diesem Lab haben Sie gelernt, wie Sie die Authentifizierung in einem Python-Client-Server-System implementieren, von grundlegenden Konzepten bis hin zu fortgeschrittenen Techniken. Sie haben erfolgreich:

  1. Die grundlegenden Konzepte der Authentifizierung in Client-Server-Systemen verstanden
  2. Die grundlegende Benutzername- und Passwort-Authentifizierung mithilfe von Flask-Sitzungen implementiert
  3. Die Sicherheit mit tokenbasierter Authentifizierung unter Verwendung von JWT verbessert
  4. Sicheres Passwort-Hashing mit bcrypt hinzugefügt
  5. Rollenbasierte Zugriffskontrolle für verschiedene Benutzertypen implementiert

Diese Fähigkeiten bieten eine solide Grundlage für die Erstellung sicherer Python-Anwendungen. Denken Sie daran, dass die Authentifizierung nur ein Aspekt der Anwendungssicherheit ist - in Produktionssystemen sollten Sie auch Folgendes berücksichtigen:

  • Verwendung von HTTPS für die gesamte Kommunikation
  • Implementierung der Ratenbegrenzung (Rate Limiting), um Brute-Force-Angriffe zu verhindern
  • Hinzufügen von Protokollierung (Logging) und Überwachung (Monitoring) für verdächtige Aktivitäten
  • Aktualisierung aller Abhängigkeiten, um Sicherheitslücken zu beheben

Durch die Anwendung dieser Prinzipien können Sie Python-Anwendungen erstellen, die Benutzerdaten effektiv schützen und einen sicheren Betrieb gewährleisten.