diff --git a/backend/app.py b/backend/app.py index 9f3de91..eaa0c4c 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1,11 +1,15 @@ from flask import Flask, request, render_template, jsonify, redirect, send_from_directory from datenbankverbindung import lade_db_config, speichere_db_config, teste_verbindung, initialisiere_admin_user +from datetime import datetime, timedelta +from jwt import decode, ExpiredSignatureError, InvalidTokenError import time import os +import jwt app = Flask(__name__) app.template_folder = "templates" +SECRET_KEY = "bitte_hier_dein_geheimes_passwort_setzen" # Achtung: später z. B. aus .env CONFIG_PATH = "config/db_config.json" MAX_WAIT = 30 # In Sekunden @@ -74,7 +78,7 @@ def serve_react(path): @app.route('/api/login', methods=['POST']) def login(): - from mysql.connector import connect, Error + from mysql.connector import connect from werkzeug.security import check_password_hash data = request.get_json() @@ -91,22 +95,64 @@ def login(): conn.close() if user and check_password_hash(user['password'], password): + # JWT generieren + payload = { + "username": user['username'], + "role": user['role'], + "exp": datetime.utcnow() + timedelta(minutes=60) # Token läuft nach 60 Min ab + } + token = jwt.encode(payload, SECRET_KEY, algorithm="HS256") + return jsonify({ - "token": "mock-token", + "token": token, "role": user['role'] }) return jsonify({"message": "Login fehlgeschlagen"}), 401 - except Error as e: - print("[Fehler bei /api/login]:", e) + except Exception as e: + print("[Login-Fehler]", e) return jsonify({"message": "Serverfehler"}), 500 + @app.route('/api/logout', methods=['POST']) def logout(): # Aktuell macht das nichts, aber der Client bekommt Bestätigung return jsonify({"message": "Logout erfolgreich"}) +def verify_token(): + auth_header = request.headers.get("Authorization", "") + if not auth_header.startswith("Bearer "): + return None + + token = auth_header.replace("Bearer ", "") + try: + decoded = decode(token, SECRET_KEY, algorithms=["HS256"]) + return decoded # enthält z. B. username, role, exp + except ExpiredSignatureError: + print("[JWT] Token abgelaufen") + return None + except InvalidTokenError: + print("[JWT] Ungültiger Token") + return None + +@app.route('/api/tools/md5', methods=['POST']) +def md5_tool(): + user = verify_token() + if not user: + return jsonify({"message": "Nicht autorisiert"}), 401 + + data = request.get_json() + input_text = data.get("text", "") + + import hashlib + result = hashlib.md5(input_text.encode()).hexdigest() + + return jsonify({ + "username": user['username'], + "hash": result + }) + if __name__ == '__main__': os.makedirs("config", exist_ok=True) diff --git a/backend/requirements.txt b/backend/requirements.txt index 954f173..f041604 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,4 +1,5 @@ flask flask-cors mysql-connector-python -werkzeug>=2.3 \ No newline at end of file +werkzeug>=2.3 +PyJWT diff --git a/frontend/src/services/api.js b/frontend/src/services/api.js index b0d0684..dded7bb 100644 --- a/frontend/src/services/api.js +++ b/frontend/src/services/api.js @@ -4,7 +4,8 @@ const instance = axios.create({ baseURL: import.meta.env.VITE_API_URL, }); -instance.interceptors.request.use(config => { +// 🔐 1. Token automatisch mitsenden (Request-Interceptor) +instance.interceptors.request.use((config) => { const token = localStorage.getItem('token'); if (token) { config.headers.Authorization = `Bearer ${token}`; @@ -12,4 +13,16 @@ instance.interceptors.request.use(config => { return config; }); +// 🔁 2. Fehler automatisch behandeln (Response-Interceptor) +instance.interceptors.response.use( + response => response, + error => { + if (error.response && error.response.status === 401) { + localStorage.clear(); + window.location.href = '/login'; // automatisch ausloggen + } + return Promise.reject(error); + } +); + export default instance;