Page HTML avec validation client et gestion de fichier
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Page d'inscription</title>
{% load static %}
<script src="{% static 'js/jquery.min.js' %}"></script>
<link href="{% static 'bootstrap/css/bootstrap.min.css' %}" rel="stylesheet">
<script src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script>
<script src="{% static 'layer/layer.js' %}"></script>
<style>
body {
font-family: 'Segoe UI', Tahoma, sans-serif;
margin: 0;
padding: 0;
background: url('https://pegasus.epweike.com/Public/uploads/introduce/55dc0c8c010df.jpg') no-repeat center center fixed;
background-size: cover;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.card-container {
background: rgba(255, 255, 255, 0.95);
border-radius: 12px;
box-shadow: 0 8px 30px rgba(0,0,0,0.15);
padding: 25px;
width: 100%;
max-width: 650px;
}
.form-group {
margin-bottom: 22px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
}
.form-control {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 6px;
transition: border-color 0.3s;
}
.form-control:focus {
border-color: #007bff;
box-shadow: 0 0 0 0.2rem rgba(0,123,255,0.25);
}
.btn-submit {
background: #007bff;
color: white;
padding: 12px 24px;
border: none;
border-radius: 6px;
cursor: pointer;
width: 100%;
font-weight: 500;
transition: background 0.3s;
}
.btn-submit:hover {
background: #0056b3;
}
</style>
</head>
<body>
<div class="card-container">
<h1 class="text-center">Créer un compte</h1>
<div class="form-group">
<label for="nom_utilisateur">Nom d'utilisateur</label>
<input type="text" id="nom_utilisateur" class="form-control" data-error="Le nom est requis">
</div>
<div class="form-group">
<label for="mot_de_passe">Mot de passe</label>
<input type="password" id="mot_de_passe" class="form-control" data-error="Le mot de passe est requis">
</div>
<div class="form-group">
<label for="confirmation_mot">Confirmer le mot de passe</label>
<input type="password" id="confirmation_mot" class="form-control" data-error="La confirmation est requise">
</div>
<div class="form-group">
<label for="email">Adresse e-mail</label>
<input type="email" id="email" class="form-control" data-error="L'email est requis">
</div>
<div class="form-group">
<label for="avatar">Télécharger l'avatar</label>
<img src="/static/img/default.png" id="preview_avatar" width="120" alt="Aperçu avatar">
<input type="file" id="avatar" style="display: none" accept="image/*">
</div>
<div class="form-group">
<button class="btn-submit">Soumettre</button>
</div>
</div>
<script>
document.getElementById('avatar').addEventListener('change', function() {
const reader = new FileReader();
const file = this.files[0];
reader.readAsDataURL(file);
reader.onload = function() {
document.getElementById('preview_avatar').src = reader.result;
};
});
document.querySelector('.btn-submit').addEventListener('click', function() {
const champs = ['nom_utilisateur', 'mot_de_passe', 'confirmation_mot', 'email'];
for (const champ of champs) {
if (!document.getElementById(champ).value) {
layer.msg(document.getElementById(champ).dataset.error);
return;
}
}
if (document.getElementById('mot_de_passe').value !== document.getElementById('confirmation_mot').value) {
layer.msg('Les mots de passe ne correspondent pas');
return;
}
const formData = new FormData();
formData.append('nom_utilisateur', document.getElementById('nom_utilisateur').value);
formData.append('mot_de_passe', document.getElementById('mot_de_passe').value);
formData.append('confirmation_mot', document.getElementById('confirmation_mot').value);
formData.append('email', document.getElementById('email').value);
formData.append('avatar', document.getElementById('avatar').files[0]);
formData.append('csrfmiddlewaretoken', '{{ csrf_token }}');
fetch('', {
method: 'POST',
body: formData,
headers: {
'X-CSRFToken': '{{ csrf_token }}'
}
})
.then(response => response.json())
.then(data => {
if (data.code === 200) {
layer.msg(data.message, {}, () => location.href = data.url);
} else {
layer.msg(data.message);
}
});
});
</script>
</body>
</html>
Back end Django - Gestion de l'inscription
from django.http import JsonResponse
from django.shortcuts import render
from . import models
import hashlib
from django.conf import settings
def inscription(request):
if request.method == 'POST':
reponse = {'code': 200, 'message': 'Inscription réussie', 'donnees': []}
nom = request.POST.get('nom_utilisateur')
mot_passe = request.POST.get('mot_de_passe')
confirmation = request.POST.get('confirmation_mot')
email = request.POST.get('email')
avatar = request.FILES.get('avatar')
if not nom:
reponse['code'] = 400
reponse['message'] = 'Nom d\'utilisateur requis'
return JsonResponse(reponse)
if not mot_passe:
reponse['code'] = 401
reponse['message'] = 'Mot de passe requis'
return JsonResponse(reponse)
if not confirmation:
reponse['code'] = 402
reponse['message'] = 'Confirmation requise'
return JsonResponse(reponse)
if mot_passe != confirmation:
reponse['code'] = 403
reponse['message'] = 'Mots de passe non correspondants'
return JsonResponse(reponse)
if not email:
reponse['code'] = 404
reponse['message'] = 'Adresse email requise'
return JsonResponse(reponse)
mot_passe_hache = hasher_mot_de_passe(mot_passe)
donnees = {
'username': nom,
'password': mot_passe_hache[:16],
'email': email
}
if avatar:
donnees['avatar'] = avatar
models.Utilisateur.objects.create(**donnees)
reponse['url'] = '/connexion/'
return JsonResponse(reponse)
return render(request, 'inscription.html')
Hashage de mot de passe avec salage
def hasher_mot_de_passe(password):
m = hashlib.md5()
mot_passe_salé = password + settings.SECRET_KEY
m.update(mot_passe_salé.encode('utf-8'))
return m.hexdigest()
Connexion utilisateur avec captcha
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Connexion</title>
{% load static %}
<script src="{% static 'js/jquery.min.js' %}"></script>
<link href="{% static 'bootstrap/css/bootstrap.min.css' %}" rel="stylesheet">
<script src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script>
<script src="{% static 'layer/layer.js' %}"></script>
<style>
body {
font-family: 'Segoe UI', Tahoma, sans-serif;
margin: 0;
padding: 0;
background: url('https://pegasus.epweike.com/Public/uploads/introduce/55dc0c8c010df.jpg') no-repeat center center fixed;
background-size: cover;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.card-container {
background: rgba(255, 255, 255, 0.95);
border-radius: 12px;
box-shadow: 0 8px 30px rgba(0,0,0,0.15);
padding: 25px;
width: 100%;
max-width: 650px;
}
.captcha-container {
display: flex;
gap: 15px;
margin-bottom: 20px;
}
.captcha-image {
width: 160px;
height: 40px;
border: 1px solid #ddd;
border-radius: 4px;
}
</style>
</head>
<body>
<div class="card-container">
<h1 class="text-center">Connexion sécurisée</h1>
<div class="form-group">
<label for="nom_utilisateur">Nom d'utilisateur</label>
<input type="text" id="nom_utilisateur" class="form-control" data-error="Nom requis">
</div>
<div class="form-group">
<label for="mot_de_passe">Mot de passe</label>
<input type="password" id="mot_de_passe" class="form-control" data-error="Mot de passe requis">
</div>
<div class="form-group captcha-container">
<label for="captcha">Code de sécurité</label>
<input type="text" id="captcha" class="form-control" data-error="Code requis" style="width: 60%">
<img src="/generer-captcha/" class="captcha-image" id="captcha-image">
</div>
<div class="form-group">
<button class="btn-submit">Se connecter</button>
</div>
</div>
<script>
document.querySelector('.btn-submit').addEventListener('click', function() {
const champs = ['nom_utilisateur', 'mot_de_passe', 'captcha'];
for (const champ of champs) {
if (!document.getElementById(champ).value) {
layer.msg(document.getElementById(champ).dataset.error);
return;
}
}
if (document.getElementById('captcha').value.length !== 5) {
layer.msg('Code de sécurité invalide');
return;
}
fetch('', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}'
},
body: JSON.stringify({
nom_utilisateur: document.getElementById('nom_utilisateur').value,
mot_de_passe: document.getElementById('mot_de_passe').value,
captcha: document.getElementById('captcha').value,
csrfmiddlewaretoken: '{{ csrf_token }}'
})
})
.then(response => response.json())
.then(data => {
if (data.code === 200) {
layer.msg(data.message, {}, () => location.href = data.url);
} else {
layer.msg(data.message);
}
});
});
</script>
</body>
</html>
Backend Django - Connexion avec vérification captcha
from django.http import JsonResponse
from django.shortcuts import render
from . import models
import hashlib
from .utils import generer_captcha
def connexion(request):
if request.method == 'POST':
reponse = {'code': 200, 'message': 'Connexion réussie, redirection dans 3s', 'donnees': []}
nom = request.POST.get('nom_utilisateur')
mot_passe = request.POST.get('mot_de_passe')
code_saisi = request.POST.get('captcha')
if not nom:
reponse['code'] = 400
reponse['message'] = 'Nom d\'utilisateur requis'
return JsonResponse(reponse)
if not mot_passe:
reponse['code'] = 401
reponse['message'] = 'Mot de passe requis'
return JsonResponse(reponse)
code_session = request.session.get('captcha_code', '').upper()
if code_saisi.upper() != code_session:
reponse['code'] = 402
reponse['message'] = 'Code de sécurité incorrect'
return JsonResponse(reponse)
mot_passe_hache = hasher_mot_de_passe(mot_passe)
utilisateur = models.Utilisateur.objects.filter(
username=nom,
password=mot_passe_hache[:16]
).first()
if not utilisateur:
reponse['code'] = 403
reponse['message'] = 'Identifiants invalides'
return JsonResponse(reponse)
request.session['utilisateur_id'] = utilisateur.pk
request.session['nom_utilisateur'] = utilisateur.username
reponse['url'] = '/accueil/'
return JsonResponse(reponse)
return render(request, 'connexion.html')
Génération de captcha
from PIL import Image, ImageDraw, ImageFont
from io import BytesIO
import random
from django.http import HttpResponse
def generer_couleur_aleatoire():
return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
def generer_captcha(request):
largeur, hauteur = 160, 40
image = Image.new('RGB', (largeur, hauteur), generer_couleur_aleatoire())
dessin = ImageDraw.Draw(image)
police = ImageFont.truetype('static/font/yun.ttf', 28)
caracteres = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
code = ''.join(random.choice(caracteres) for _ in range(5))
request.session['captcha_code'] = code
for i, char in enumerate(code):
x = 10 + i * 25
y = random.randint(5, 15)
dessin.text((x, y), char, generer_couleur_aleatoire(), police)
buffer = BytesIO()
image.save(buffer, format='PNG')
return HttpResponse(buffer.getvalue(), content_type='image/png')
Interface principale avec navigation et modal
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Accueil</title>
{% load static %}
<script src="{% static 'js/jquery.min.js' %}"></script>
<link href="{% static 'bootstrap/css/bootstrap.min.css' %}" rel="stylesheet">
<script src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script>
<script src="{% static 'layer/layer.js' %}"></script>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="#">Réseau social professionnel</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto">
<li class="nav-item"><a class="nav-link active" href="#">Articles</a></li>
<li class="nav-item"><a class="nav-link" href="#">Catégories</a></li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown">Découvrir</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Communauté</a></li>
<li><a class="dropdown-item" href="#">Tendances</a></li>
</ul>
</li>
</ul>
<form class="d-flex">
<input class="form-control me-2" type="search" placeholder="Rechercher">
<button class="btn btn-outline-light" type="submit">Rechercher</button>
</form>
<ul class="navbar-nav ms-auto">
{% if request.session.nom_utilisateur %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="userMenu" data-bs-toggle="dropdown">
{{ request.session.nom_utilisateur }}
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="#" data-bs-toggle="modal" data-bs-target="#modalChangerMotDePasse">Modifier mot de passe</a></li>
<li><a class="dropdown-item" href="/deconnexion/">Se déconnecter</a></li>
</ul>
</li>
{% else %}
<li class="nav-item"><a class="nav-link" href="/connexion/">Connexion</a></li>
<li class="nav-item"><a class="nav-link" href="/inscription/">Inscription</a></li>
{% endif %}
</ul>
</div>
</div>
</nav>
<div class="modal fade" id="modalChangerMotDePasse" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Changer le mot de passe</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Nom d'utilisateur</label>
<input type="text" class="form-control" value="{{ request.session.nom_utilisateur }}" readonly>
</div>
<div class="mb-3">
<label class="form-label">Ancien mot de passe</label>
<input type="password" id="ancien_mot" class="form-control" data-error="Champ requis">
</div>
<div class="mb-3">
<label class="form-label">Nouveau mot de passe</label>
<input type="password" id="nouveau_mot" class="form-control" data-error="Champ requis">
</div>
<div class="mb-3">
<label class="form-label">Confirmer</label>
<input type="password" id="confirmation" class="form-control" data-error="Champ requis">
</div>
<button id="btnValiderMotDePasse" class="btn btn-primary">Valider</button>
</div>
</div>
</div>
</div>
<script>
document.getElementById('btnValiderMotDePasse').addEventListener('click', function() {
const ancien = document.getElementById('ancien_mot').value;
const nouveau = document.getElementById('nouveau_mot').value;
const confirmation = document.getElementById('confirmation').value;
const champs = ['ancien_mot', 'nouveau_mot', 'confirmation'];
for (const champ of champs) {
if (!document.getElementById(champ).value) {
layer.msg(document.getElementById(champ).dataset.error);
return;
}
}
if (nouveau !== confirmation) {
layer.msg('Les mots de passe ne correspondent pas');
return;
}
fetch('/modifier-mot-de-passe/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}'
},
body: JSON.stringify({
ancien_mot: ancien,
nouveau_mot: nouveau,
confirmation: confirmation,
csrfmiddlewaretoken: '{{ csrf_token }}'
})
})
.then(response => response.json())
.then(data => {
if (data.code === 200) {
layer.msg(data.message, {}, () => location.reload());
} else {
layer.msg(data.message);
}
});
});
</script>
</body>
</html>
Backend Django - Modification de mot de passe
from django.http import JsonResponse
from django.contrib.auth.hashers import make_password
from . import models
def modifier_mot_de_passe(request):
if request.method == 'POST':
reponse = {'code': 200, 'message': 'Mot de passe modifié avec succès', 'donnees': []}
ancien = request.POST.get('ancien_mot')
nouveau = request.POST.get('nouveau_mot')
confirmation = request.POST.get('confirmation')
if nouveau != confirmation:
reponse['code'] = 400
reponse['message'] = 'Les mots de passe ne correspondent pas'
return JsonResponse(reponse)
utilisateur = models.Utilisateur.objects.get(
pk=request.session.get('utilisateur_id')
)
if not hasher_mot_de_passe(ancien) == utilisateur.password[:16]:
reponse['code'] = 401
reponse['message'] = 'Ancien mot de passe incorrect'
return JsonResponse(reponse)
utilisateur.password = hasher_mot_de_passe(nouveau)[:16]
utilisateur.save()
return JsonResponse(reponse)