Mini app authentification avec token jwt
This commit is contained in:
parent
e9efcdb236
commit
941cd1f89e
16
README.md
16
README.md
@ -1,2 +1,18 @@
|
|||||||
# Appli_demo
|
# Appli_demo
|
||||||
|
Petite app utilisant l'authentification avec une génération de token jwt.
|
||||||
|
|
||||||
|
## Installation et prérequis
|
||||||
|
Le projet est codé en Python3 il est donc necessaire d'avoir Python3 d'installer sur sa machine. Pour vérifier si
|
||||||
|
Python3 est bien installer `python3 --version`
|
||||||
|
|
||||||
|
Dans un terminal utiliser `pip install -r requirements.txt` (_de préférence dans un environement
|
||||||
|
virtuel_) afin d'installer les librairies necessaires au bon fonctionnement du projet.
|
||||||
|
|
||||||
|
## Lancement du projet
|
||||||
|
Ensuite ouvrez deux terminal et placer vous à la racine du projet.
|
||||||
|
|
||||||
|
Dans le premier taper `flask run` afin de lancer le serveur web.
|
||||||
|
|
||||||
|
Et dans l'autre `python3 requetes.py` afin de lancer une batterie de requête pour vérifier le bon
|
||||||
|
fonctionnement du projet.
|
||||||
|
|
||||||
|
146
app.py
Normal file
146
app.py
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
from flask import Flask, request, jsonify, make_response, session, render_template, abort
|
||||||
|
from flask_restful import Api, Resource
|
||||||
|
import jwt
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
app.config['SECRET_KEY'] = 'secret'
|
||||||
|
|
||||||
|
api = Api(app)
|
||||||
|
|
||||||
|
classes = {'1':
|
||||||
|
{
|
||||||
|
'nom': 'A1',
|
||||||
|
'nombre_eleve': '15',
|
||||||
|
'eleves': {
|
||||||
|
'1': ['Mariue', 'Julien', '12'],
|
||||||
|
'2': ['Koene', 'Morice', '13'],
|
||||||
|
'3': ['Moatir', 'Pierre', '12'],
|
||||||
|
'4': ['Poiti', 'Marc', '12'],
|
||||||
|
'5': ['Areop', 'Tome', '12'],
|
||||||
|
'6': ['Cenois', 'Louis', '13'],
|
||||||
|
'7': ['Quotine', 'Maxime', '12'],
|
||||||
|
'8': ['Reval', 'Adrien', '12'],
|
||||||
|
'9': ['Fonduri', 'Juliette', '11'],
|
||||||
|
'10': ['Graconti', 'Marie', '12'],
|
||||||
|
'11': ['Henvio', 'Louise', '12'],
|
||||||
|
'12': ['Kenano', 'Bertran', '14'],
|
||||||
|
'13': ['Vertille', 'Jean-Pierre', '12'],
|
||||||
|
'14': ['Provern', 'Jean', '14'],
|
||||||
|
'15': ['Secinoi', 'Celine', '12'],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'2':
|
||||||
|
{
|
||||||
|
'nom': 'A2',
|
||||||
|
'nombre_eleve': '5',
|
||||||
|
'eleves': {
|
||||||
|
'1': ['Benar', 'Leo', '15'],
|
||||||
|
'2': ['Grovin', 'Benois', '16'],
|
||||||
|
'3': ['Xeroi', 'Amelie', '15'],
|
||||||
|
'4': ['Amonie', 'Julien', '14'],
|
||||||
|
'5': ['Surois', 'Camille', '15']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def token_required(func):
|
||||||
|
@wraps(func)
|
||||||
|
def decorated(*args, **kwargs):
|
||||||
|
|
||||||
|
token = None
|
||||||
|
if 'token' in request.headers:
|
||||||
|
token = request.headers['token']
|
||||||
|
|
||||||
|
if not token:
|
||||||
|
return jsonify({'Alert!': 'Pas de Token!'})
|
||||||
|
# abort(403, 'pas de token')
|
||||||
|
try:
|
||||||
|
payload = jwt.decode(token, app.config['SECRET_KEY'])
|
||||||
|
except:
|
||||||
|
# abort(403, 'token invalid')
|
||||||
|
return make_response(jsonify({'Alert!': 'Token invalid!'}))
|
||||||
|
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
|
return decorated
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def home():
|
||||||
|
if not session.get('logged_in'):
|
||||||
|
return render_template('login.html')
|
||||||
|
else:
|
||||||
|
return 'Déjà connecté'
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/public')
|
||||||
|
def public():
|
||||||
|
return 'Vous êtes bien sur la page public !'
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/auth', methods=['GET'])
|
||||||
|
@token_required
|
||||||
|
def auth():
|
||||||
|
return 'Vous êtes bien sur la page auth !'
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/login', methods=['POST'])
|
||||||
|
def login():
|
||||||
|
if request.form['username'] and request.form['password'] == '123':
|
||||||
|
session['logged_in'] = True
|
||||||
|
token = jwt.encode({
|
||||||
|
'user': 0
|
||||||
|
# 'expiration': str(datetime.utcnow() + timedelta(seconds=30))
|
||||||
|
},
|
||||||
|
app.config['SECRET_KEY'], algorithm="HS256")
|
||||||
|
return jsonify({'token': token.decode('utf-8')})
|
||||||
|
else:
|
||||||
|
return make_response('Unable to verify', 403, {'WWW-Authenticate': 'Basic realm:"Authentication Failed!'})
|
||||||
|
|
||||||
|
|
||||||
|
class Classes(Resource):
|
||||||
|
@token_required
|
||||||
|
def get(self):
|
||||||
|
return classes
|
||||||
|
|
||||||
|
@token_required
|
||||||
|
def post(self):
|
||||||
|
return classes
|
||||||
|
|
||||||
|
|
||||||
|
api.add_resource(Classes, "/classes")
|
||||||
|
|
||||||
|
|
||||||
|
class Classe(Resource):
|
||||||
|
@token_required
|
||||||
|
def get(self, id_classe):
|
||||||
|
return classes[id_classe]
|
||||||
|
|
||||||
|
@token_required
|
||||||
|
def post(self, id_classe):
|
||||||
|
return classes[id_classe]
|
||||||
|
|
||||||
|
|
||||||
|
api.add_resource(Classe, "/classes/<string:id_classe>")
|
||||||
|
|
||||||
|
|
||||||
|
class Eleve(Resource):
|
||||||
|
@token_required
|
||||||
|
def get(self, id_classe, id_eleve):
|
||||||
|
return classes[id_classe]['eleves'][id_eleve]
|
||||||
|
|
||||||
|
@token_required
|
||||||
|
def post(self, id_classe, id_eleve):
|
||||||
|
return classes[id_classe]['eleves'][id_eleve]
|
||||||
|
|
||||||
|
|
||||||
|
api.add_resource(Eleve, "/<string:id_classe>/<string:id_eleve>")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/reset")
|
||||||
|
def reset():
|
||||||
|
session.clear()
|
35
model/data.py
Normal file
35
model/data.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# data = {'1':
|
||||||
|
# {
|
||||||
|
# 'nom': 'A1',
|
||||||
|
# 'nombre_eleve': '15',
|
||||||
|
# 'eleves': {
|
||||||
|
# '1': ['Mariue', 'Julien', '12'],
|
||||||
|
# '2': ['Koene', 'Morice', '13'],
|
||||||
|
# '3': ['Moatir', 'Pierre', '12'],
|
||||||
|
# '4': ['Poiti', 'Marc', '12'],
|
||||||
|
# '5': ['Areop', 'Tome', '12'],
|
||||||
|
# '6': ['Cenois', 'Louis', '13'],
|
||||||
|
# '7': ['Quotine', 'Maxime', '12'],
|
||||||
|
# '8': ['Reval', 'Adrien', '12'],
|
||||||
|
# '9': ['Fonduri', 'Juliette', '11'],
|
||||||
|
# '10': ['Graconti', 'Marie', '12'],
|
||||||
|
# '11': ['Henvio', 'Louise', '12'],
|
||||||
|
# '12': ['Kenano', 'Bertran', '14'],
|
||||||
|
# '13': ['Vertille', 'Jean-Pierre', '12'],
|
||||||
|
# '14': ['Provern', 'Jean', '14'],
|
||||||
|
# '15': ['Secinoi', 'Celine', '12'],
|
||||||
|
# }
|
||||||
|
# },
|
||||||
|
# '2':
|
||||||
|
# {
|
||||||
|
# 'nom': 'A2',
|
||||||
|
# 'nombre_eleve': '5',
|
||||||
|
# 'eleve': {
|
||||||
|
# '1': ['Benar', 'Leo', '15'],
|
||||||
|
# '2': ['Grovin', 'Benois', '16'],
|
||||||
|
# '3': ['Xeroi', 'Amelie', '15'],
|
||||||
|
# '4': ['Amonie', 'Julien', '14'],
|
||||||
|
# '5': ['Surois', 'Camille', '15']
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# }
|
60
requetes.py
Normal file
60
requetes.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import requests
|
||||||
|
|
||||||
|
BASE = "http://127.0.0.1:5000/"
|
||||||
|
|
||||||
|
login = requests.post(BASE + 'login', {'username': 'leo', 'password': '123'})
|
||||||
|
|
||||||
|
token = login.json()['token']
|
||||||
|
|
||||||
|
print('Génération du token par une authentification correct: ')
|
||||||
|
print(token)
|
||||||
|
print("\n")
|
||||||
|
|
||||||
|
HEADERS = {"Authorization": f"Bearer {token}", 'token': token}
|
||||||
|
|
||||||
|
public = requests.get(BASE + "public")
|
||||||
|
print('Accès à la page public qui ne nécessite pas de token: ')
|
||||||
|
print(public.text)
|
||||||
|
print("\n")
|
||||||
|
|
||||||
|
auth = requests.get(BASE + 'auth', headers=HEADERS)
|
||||||
|
print('Accès à la page auth qui nécessite un token valide: ')
|
||||||
|
print(auth.text)
|
||||||
|
print("\n")
|
||||||
|
|
||||||
|
classes = requests.get(BASE + "classes", headers=HEADERS)
|
||||||
|
print("Les classes : ")
|
||||||
|
print(classes.json())
|
||||||
|
print("\n")
|
||||||
|
|
||||||
|
classes1 = requests.get(BASE + "classes/1", headers=HEADERS)
|
||||||
|
print("La classe 1 : ")
|
||||||
|
print(classes1.json())
|
||||||
|
print("\n")
|
||||||
|
|
||||||
|
eleve = requests.get(BASE + "2/5", headers=HEADERS)
|
||||||
|
print("L'eleve 5 de la classe 2 : ")
|
||||||
|
print(eleve.json())
|
||||||
|
print("\n")
|
||||||
|
|
||||||
|
requests.get(BASE + 'reset')
|
||||||
|
print('Reset de la session pour supprimer le token...')
|
||||||
|
|
||||||
|
login2 = requests.post(BASE + 'login', {'username': 'leo', 'password': 'azerty'})
|
||||||
|
|
||||||
|
print('Login avec un password qui ne générera pas de token... ')
|
||||||
|
print("\n")
|
||||||
|
|
||||||
|
public2 = requests.get(BASE + "public")
|
||||||
|
print('Accès à la page public qui ne nécessite pas de token: ')
|
||||||
|
print(public2.text)
|
||||||
|
print("\n")
|
||||||
|
|
||||||
|
auth2 = requests.get(BASE + 'auth')
|
||||||
|
print('Accès à la page auth qui nécessite un token valide: ')
|
||||||
|
print(auth2.json())
|
||||||
|
print("\n")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
44
requirements.txt
Normal file
44
requirements.txt
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
aniso8601==9.0.1
|
||||||
|
anyio==3.3.4
|
||||||
|
api==0.0.7
|
||||||
|
attrs==21.2.0
|
||||||
|
auth==0.5.3
|
||||||
|
blinker==1.4
|
||||||
|
certifi==2021.10.8
|
||||||
|
charset-normalizer==2.0.7
|
||||||
|
click==8.0.3
|
||||||
|
dnspython==2.1.0
|
||||||
|
eventlet==0.32.0
|
||||||
|
falcon==3.0.1
|
||||||
|
fastapi==0.70.0
|
||||||
|
Flask==2.0.2
|
||||||
|
Flask-HTTPAuth==4.5.0
|
||||||
|
Flask-JWT==0.3.2
|
||||||
|
Flask-RESTful==0.3.9
|
||||||
|
greenlet==1.1.2
|
||||||
|
gunicorn==20.1.0
|
||||||
|
idna==3.3
|
||||||
|
itsdangerous==2.0.1
|
||||||
|
Jinja2==3.0.2
|
||||||
|
JsonForm==0.0.2
|
||||||
|
jsonschema==4.1.2
|
||||||
|
JsonSir==0.0.2
|
||||||
|
MarkupSafe==2.0.1
|
||||||
|
mongoengine==0.23.1
|
||||||
|
nose==1.3.7
|
||||||
|
pydantic==1.8.2
|
||||||
|
PyJWT==1.4.2
|
||||||
|
pymongo==3.12.1
|
||||||
|
pyrsistent==0.18.0
|
||||||
|
python-dotenv==0.19.1
|
||||||
|
Python-EasyConfig==0.1.7
|
||||||
|
pytz==2021.3
|
||||||
|
PyYAML==6.0
|
||||||
|
requests==2.26.0
|
||||||
|
Resource==0.2.1
|
||||||
|
six==1.16.0
|
||||||
|
sniffio==1.2.0
|
||||||
|
starlette==0.16.0
|
||||||
|
typing-extensions==3.10.0.2
|
||||||
|
urllib3==1.26.7
|
||||||
|
Werkzeug==2.0.2
|
12
templates/base.html
Normal file
12
templates/base.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>{{ title }}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Mini-App classe</h1>
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
|
</body>
|
||||||
|
</html>
|
12
templates/login.html
Normal file
12
templates/login.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block content %}
|
||||||
|
<form action="{{ url_for('login') }}" method="POST">
|
||||||
|
<label for="username">username</label>
|
||||||
|
<input type="text" name="username" placeholder="Username"> <br><br>
|
||||||
|
|
||||||
|
<label for="password">password</label>
|
||||||
|
<input type="password" name="password" placeholder="Password"> <br><br>
|
||||||
|
|
||||||
|
<input type="submit" value="Login">
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user