Technologies employées
Framework backend : SpringBoot
Spring Boot permet de créer des appplications autonomes et prêtes pour la production. Il fournit un ensemble de prérequis d'opinion ("opinionated defaults") pour le configuration de Spring et des bibliothèques tierces, ce qui évite une configuration manuelle fastidieuse. Il est conçu pour faciliter le démarrage rapide des projets et permet de se concentrer sur la logique métier plutôt que sur l'infrastructure.
Framework frontend : Vue.js
Vue.js est un framework JavaScript progressif pour la construction d'interfaces utilisateur. Son modèle de programmation basé sur des composants et son système réactif de liaison de données permettent de créer des applications web modernes et réactives. Sa nature progressive permet de l'adopter de manière incrémentielle, que ce soit pour enrichir une partie d'une page existante ou pour développer une application complète (SPA).
Couche de persistence : MyBatis-Plus
MyBatis-Plus est un complément puissant à MyBatis qui fournit des fonctionnalités supplémentaires pour simplifier les opérations CRUD et le développement de la couche d'accès aux données. Il inclut des outils de génération de code, des fonctionnalités avancées de pagination et de requêtes conditionnelles, ainsi qu'une prise en charge optimisée pour les bases de données relationnelles courantes.
Test du système
La phase de test vise à identifier les défauts et les vulnérabilités du système en le soumettant à des scénarios d'utilisation variés. L'objectif est de garantir que le produit final répond aux spécifications fonctionnelles et non fonctionnelles définies et offre une expérience utilisateur fiable.
Objectifs des tests
Les tests constituent une étape cruciale dans le cycle de développement pour assurer la qualité et la robustesse du système. Ils visent à simuler les conditions d'utilisation réelles afin de détecter les problèmes avant la mise en production. Cette démarche permet de valider la conformité aux exigences, d'identifier les écarts par rapport au cahier des charges et d'améliorer la satisfaction de l'utilisateur final.
Tests fonctionnels
Les tests fonctionnels sont réalisés sur les différents modules du système. Ils impliquent une approche de boîte noire, où le système est testé en fonction de ses entrées et sorties sans examen du code interne. Les cas de test sont conçus pour vérifier les fonctionnalités clés, les règles de validation et la gestion des erreurs.
Scénario de test : Authentification
| Données d'entrée | Résultat attendu | Résultat observé | Analyse |
|---|---|---|---|
| Identifiant : admin Mot de passe : secret123 Code : valide | Connexion réussie | Connexion réussie | Conforme |
| Identifiant : admin Mot de passe : mauvais Code : valide | Message d'erreur sur le mot de passe | Message d'erreur sur le mot de passe | Conforme |
| Identifiant : admin Mot de passe : secret123 Code : invalide | Message d'erreur sur le code | Message d'erreur sur le code | Conforme |
| Identifiant : (vide) Mot de passe : secret123 Code : valide | Champ identifiant obligatoire | Champ identifiant obligatoire | Conforme |
Scénario de test : Gestion des utilisateurs
| Action | Résultat attendu | Résultat observé | Analyse |
|---|---|---|---|
| Créer un nouvel utilisateur avec des informations valides | Utilisateur ajouté à la liste | Utilisateur visible dans la liste | Conforme |
| Modifier les informations d'un utilisateur existant | Mise à jour des informations | Informations mises à jour dans l'affichage | Conforme |
| Supprimer un utilisateur sélectionné | Utilisateur supprimé après confirmation | Utilisateur introuvable après suppression | Conforme |
| Tenter de créer un utilisateur sans remplir le champ obligatoire "nom d'utilisateur" | Message d'erreur indiquant que le champ est obligatoire | Message d'erreur indiquant que le champ est obligatoire | Conforme |
Conclusion des tests
L'ensemble des tests fonctionnels a été exécuté avec succès, démontrant que les modules principaux du système se comportent conformément aux attentes. Les scénarios d'erreur et les cas limites ont été correctement gérés. Ces résultats valident la solidité de l'implémentation et la qualité de l'expérience utilisateur proposée.
Extrait de code
Voici un exemple de méthode pour gérer l'authentification et de génération de jeton (token) :
@IgnoreAuth
@PostMapping(value = "/connexion")
public R seConnecter(String identifiant, String motDePasse, String codeVerification, HttpServletRequest requete) {
UtilisateurEntity utilisateur = serviceUtilisateur.selectOne(new EntityWrapper<UtilisateurEntity>().eq("identifiant", identifiant));
if(utilisateur == null || !utilisateur.getMotDePasse().equals(motDePasse)) {
return R.error("Identifiants incorrects");
}
String jeton = serviceJeton.genererJeton(utilisateur.getId(), identifiant, "utilisateurs", utilisateur.getRole());
return R.ok().put("jeton", jeton);
}
@Override
public String genererJeton(Long idUtilisateur, String identifiant, String nomTable, String role) {
JetonEntity entiteJeton = this.selectOne(new EntityWrapper<JetonEntity>().eq("id_utilisateur", idUtilisateur).eq("role", role));
String nouveauJeton = UtilitaireCommun.chaineAleatoire(32);
Calendar calendrier = Calendar.getInstance();
calendrier.add(Calendar.HOUR_OF_DAY, 1);
if(entiteJeton != null) {
entiteJeton.setJeton(nouveauJeton);
entiteJeton.setDateExpiration(calendrier.getTime());
this.updateById(entiteJeton);
} else {
this.insert(new JetonEntity(idUtilisateur, identifiant, nomTable, role, nouveauJeton, calendrier.getTime()));
}
return nouveauJeton;
}
Un intercepteur pour vérifier l'autorisation via le jeton :
@Component
public class IntercepteurAutorisation implements HandlerInterceptor {
public static final String EN_TETE_JETON = "Jeton";
@Autowired
private ServiceJeton serviceJeton;
@Override
public boolean preHandle(HttpServletRequest requete, HttpServletResponse reponse, Object handler) throws Exception {
reponse.setHeader("Access-Control-Allow-Origin", requete.getHeader("Origin"));
reponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
reponse.setHeader("Access-Control-Allow-Headers", "Origin, Jeton, Content-Type, Accept");
if (requete.getMethod().equals(RequestMethod.OPTIONS.name())) {
reponse.setStatus(HttpStatus.OK.value());
return false;
}
IgnoreAuth annotation;
if (handler instanceof HandlerMethod) {
annotation = ((HandlerMethod) handler).getMethodAnnotation(IgnoreAuth.class);
} else {
return true;
}
String jeton = requete.getHeader(EN_TETE_JETON);
if(annotation != null) {
return true;
}
JetonEntity entiteJeton = null;
if(StringUtils.isNotBlank(jeton)) {
entiteJeton = serviceJeton.obtenirEntiteParJeton(jeton);
}
if(entiteJeton != null) {
requete.getSession().setAttribute("utilisateurId", entiteJeton.getIdUtilisateur());
requete.getSession().setAttribute("role", entiteJeton.getRole());
return true;
}
reponse.setCharacterEncoding("UTF-8");
reponse.setContentType("application/json; charset=utf-8");
PrintWriter ecrivain = reponse.getWriter();
ecrivain.print("{\"code\":401, \"msg\":\"Veuillez vous connecter\"}");
ecrivain.close();
return false;
}
}
Structure de base de données
-- Table pour stocker les jetons d'authentification
DROP TABLE IF EXISTS `jeton_auth`;
CREATE TABLE `jeton_auth` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Clé primaire',
`id_utilisateur` bigint(20) NOT NULL COMMENT 'Identifiant de l''utilisateur',
`nom_utilisateur` varchar(100) NOT NULL COMMENT 'Nom de l''utilisateur',
`nom_table` varchar(100) DEFAULT NULL COMMENT 'Table source',
`role` varchar(100) DEFAULT NULL COMMENT 'Rôle de l''utilisateur',
`jeton` varchar(200) NOT NULL COMMENT 'Valeur du jeton',
`date_creation` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Date de création',
`date_expiration` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Date d''expiration',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='Table des jetons d''authentification';
-- Exemples d'enregistrements
INSERT INTO `jeton_auth` VALUES (1, 1001, 'conducteur01', 'utilisateurs', 'conducteur', 'abc123xyz789def456ghi012jkl345mno', '2023-05-10 09:00:00', '2023-05-10 17:00:00');
INSERT INTO `jeton_auth` VALUES (2, 1002, 'admin_systeme', 'utilisateurs', 'administrateur', 'pqr678stu901vwx234yza567bcd890efg', '2023-05-10 10:15:00', '2023-05-11 10:15:00');