Développement d'un Système Java pour la Planification Personnalisée de Voyages

Technologies Clés Utilisées

Cadre Dorsal: Spring Boot

Spring Boot est un environnement de développement Java qui simplifie la création d'applications robustes et autonomes. Il intègre des serveurs d'applications populaires comme Tomcat, Jetty ou Undertow directement dans le paquet exécutable, éliminant ainsi le besoin de déploiements externes. Sa fonctionnalité principale réside dans sa capacité à l'auto-configuration, qui interprète les dépendances du projet et configure automatiquement les composants nécessaires, réduisant considérablement la configuration manuelle. Spring Boot propose également un vaste écosystème de modules, tels que Spring Data pour l'accès aux données, Spring Security pour l'authentification et l'autorisation, et Spring Cloud pour les architectures de microservices, permettatn un développement accéléré et une maintenance facilitée des applications d'entreprise.

Cadre Frontal: Vue.js

Vue.js est un cadre JavaScript progressif pour la construction d'interfaces utilisateur interactives. Il excelle dans la création de Single Page Applications (SPA) grâce à son approche basée sur les composants et son système de réactivité. Au cœur de Vue.js se trouve le DOM virtuel, une représentation légère du DOM réel qui permet des mises à jour efficaces de l'interface graphique. Lorsque les données de l'application changent, Vue.js recalcule intelligemment les différences et applique uniquement les modifications nécessaires au DOM, optimisant ainsi les performances. Sa syntaxe claire et sa courbe d'apprentissage douce en font un choix populaire pour les développeurs cherchant à créer des expériences utilisateur dynamiques et maintenables.

Persistance des Données: MyBatis-Plus

MyBatis-Plus est un outil d'amélioration du cadre MyBatis, conçu pour rationaliser les opérations de base de données en Java. Il agit comme une couche d'abstraction supplémentaire qui simplifie les requêtes SQL, offrant des opérations CRUD (Créer, Lire, Mettre à jour, Supprimer) génériques sans nécessiter l'écriture de SQL répétitif. Il prend en charge une multitude de bases de données relationnelles, notamment MySQL, Oracle et PostgreSQL. MyBatis-Plus fournit également des fonctionnalités avancées telles que la pagination automatique, les requêtes dynamiques, le support du verrouillage opitmiste et un générateur de code qui peut créer automatiquement les entités, les interfaces Mapper et les fichiers de mappage XML, accélérant considérablement le développement de la couche d'accès aux données.

Stratégie de Test du Système

La phase de test est cruciale pour garantir la fiabilité et la qualité d'un système logiciel. Notre approche vise à identifier les défauts potentiels et à valider que toutes les fonctionnalités répondent aux spécifications initiales. L'objectif principal est d'offrir une expérience utilisateur fluide et sans erreur.

Objectifs des Tests Système

Les tests système ont pour but d'évaluer le comportement global de l'application dans des conditions proches de l'environnement de production. Ils vérifient la conformité aux exigences fonctionnelles et non fonctionnelles, la stabilité, la performance et la sécurité. En simulant des scénarios d'utilisation variés, nous cherchons à déceler les anomalies, à valider l'intégrité des données et à confirmer que le système est robuste et intuitif pour l'utilisateur final. Cette étape est essentielle pour s'assurer que le produit livré est de haute qualité et répond aux attentes.

Tests Fonctionnels Détaillés

Les tests fonctionnels ont été menés selon une méthodologie de boîte noire, se concentrant sur les fonctionnalités exposées à l'utilisateur sans s'immiscer dans l'implémentation interne. Nous avons élaboré des scénarios de test exhaustifs pour chaque module, incluant la validation des entrées, la gestion des erreurs et la conformité des sorties attendues.

Plan de Test pour la Connexion Utilisateur

La fonctionnalité de connexion a été rigoureusement testée pour garantir la sécurité et la précision de l'authentification. Cela inclut la validation des identifiants (nom d'utilisateur et mot de passe), la gestion des tentatives de connexion échouées et la vérification des autorisations basées sur les rôles.

Données d'Entrée Résultat Attendu Résultat Actuel Analyse
Nom utilisateur: admin, Mot de passe: admin123, Code CAPTCHA: Correct Accès au système Connexion réussie Conforme aux attentes
Nom utilisateur: admin, Mot de passe: mauvaisMdp, Code CAPTCHA: Correct Message d'erreur: "Mot de passe incorrect" Affichage: "Mot de passe incorrect, veuillez réessayer" Conforme aux attentes
Nom utilisateur: admin, Mot de passe: admin123, Code CAPTCHA: Incorrect Message d'erreur: "Code CAPTCHA incorrect" Affichage: "Le code CAPTCHA fourni est invalide" Conforme aux attentes
Nom utilisateur: Vide, Mot de passe: admin123, Code CAPTCHA: Correct Message d'erreur: "Le nom d'utilisateur est requis" Affichage: "Veuillez saisir un nom d'utilisateur" Conforme aux attentes
Nom utilisateur: admin, Mot de passe: Vide, Code CAPTCHA: Correct Message d'erreur: "Le mot de passe est requis" Affichage: "Veuillez saisir le mot de passe" Conforme aux attentes

Plan de Test pour la Gestion des Utilisateurs

Le module de gestion des utilisateurs, comprenant l'ajout, la modification, la suppression et la recherche, a été testé pour assurer la cohérence et la robustesse des opérations. Une attention particulière a été portée à la validation des champs obligatoires et à la prévention des doublons.

Données d'Entrée Résultat Attendu Résultat Actuel Analyse
Informations utilisateur valides Ajout réussi, utilisateur listé L'utilisateur apparaît dans la liste Conforme aux attentes
Modification des détails de l'utilisateur existant Modification réussie, informations mises à jour Les informations de l'utilisateur sont modifiées Conforme aux attentes
Sélection et confirmation de la suppression d'un utilisateur Le système demande confirmation, puis l'utilisateur est supprimé Le système demande confirmation, l'utilisateur n'est plus trouvé après suppression Conforme aux attentes
Tentative d'ajout d'utilisateur sans nom d'utilisateur Message d'erreur: "Nom d'utilisateur obligatoire" Affichage: "Le nom d'utilisateur ne peut être vide" Conforme aux attentes
Tentative d'ajout d'utilisateur avec un nom d'utilisateur déjà existant Échec de l'ajout, message: "Nom d'utilisateur déjà utilisé" Échec de l'ajout, affichage: "Ce nom d'utilisateur est déjà pris" Conforme aux attentes

Conclusion des Tests Système

Les tests fonctionnels, principalement conduits en boîte noire, ont confirmé que les modules du système sont conformes aux spécifications initiales. En simulant des enteractions utilisateur pour chaque fonctionnalité, nous avons validé la logique opérationnelle et l'intégrité du système. L'approche axée sur l'utilisateur a permis de garantir que l'application est non seulement fonctionnelle mais également intuitive et agréable à utiliser. Les résultats des tests démontrent que le système développé répond aux exigences de conception en termes de fonctionnalités et de performance.

Extraits de Code

Les extraits de code suivants illustrent des composants clés de l'authentification et de l'autorisation dans le système. Ils montrent comment les requêtes de connexion sont traitées, comment les jetons de sécurité sont générés et gérés, et comment l'accès aux ressources protégées est contrôlé.


// Contrôleur gérant les requêtes d'authentification
@IgnoreAuthentication
@PostMapping(value = "/authentifier")
public ServiceResponse processLoginRequest(@RequestParam String accountIdentifier, @RequestParam String credentialHash, HttpServletRequest request) {
   UserAccount user = userAccountService.findByUsername(accountIdentifier);

   if (user == null || !user.getHashedPassword().equals(credentialHash)) {
       return ServiceResponse.error("Nom d'utilisateur ou mot de passe invalide.");
   }

   String securityToken = authenticationService.generateAuthToken(user.getId(), accountIdentifier, "UserAccount", user.getRole());
   return ServiceResponse.success().withData("authToken", securityToken);
}

// Service de génération et de rafraîchissement de jetons
@Override
public String generateAuthToken(Long userId, String username, String entityType, String userRole) {
   TokenData existingToken = this.retrieveToken(userId, userRole);
   String newTokenValue = SecurityUtil.generateRandomString(40); // Génère une chaîne aléatoire unique
   
   Calendar expirationCalendar = Calendar.getInstance();
   expirationCalendar.setTime(new Date());
   expirationCalendar.add(Calendar.HOUR_OF_DAY, 2); // Jeton valide pour 2 heures

   if (existingToken != null) {
       existingToken.setTokenValue(newTokenValue);
       existingToken.setExpirationTimestamp(expirationCalendar.getTime());
       this.updateToken(existingToken);
   } else {
       TokenData newEntry = new TokenData(userId, username, entityType, userRole, newTokenValue, expirationCalendar.getTime());
       this.storeNewToken(newEntry);
   }
   return newTokenValue;
}

// Intercepteur pour la validation des accès et des jetons
@Component
public class SecurityAccessInterceptor implements HandlerInterceptor {

   public static final String AUTH_TOKEN_HEADER = "X-Auth-Token";

   @Autowired
   private AuthenticationService authenticationService;
   
   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
       // Configuration CORS
       response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
       response.setHeader("Access-Control-Max-Age", "7200");
       response.setHeader("Access-Control-Allow-Credentials", "true");
       response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, Origin, Content-Type, Accept, " + AUTH_TOKEN_HEADER);
       response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
       
       if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
           response.setStatus(HttpStatus.OK.value());
           return false;
       }
       
       IgnoreAuthentication bypassAuth;
       if (handler instanceof HandlerMethod) {
           bypassAuth = ((HandlerMethod) handler).getMethodAnnotation(IgnoreAuthentication.class);
       } else {
           return true;
       }

       if (bypassAuth != null) { // Méthode marquée pour ignorer l'authentification
           return true;
       }
       
       String receivedToken = request.getHeader(AUTH_TOKEN_HEADER);
       TokenData validatedToken = null;
       if (StringUtils.isNotBlank(receivedToken)) {
           validatedToken = authenticationService.validateAndRetrieveToken(receivedToken);
       }
       
       if (validatedToken != null && !validatedToken.isExpired()) {
           request.getSession().setAttribute("currentUserId", validatedToken.getUserId());
           request.getSession().setAttribute("currentUserRole", validatedToken.getUserRole());
           request.getSession().setAttribute("currentUserEntity", validatedToken.getEntityType());
           request.getSession().setAttribute("currentUsername", validatedToken.getUsername());
           return true;
       }
       
       // Jeton invalide ou manquant, renvoyer une erreur d'authentification
       response.setCharacterEncoding("UTF-8");
       response.setContentType("application/json; charset=utf-8");
       try (PrintWriter writer = response.getWriter()) {
           writer.print(JSON.toJSONString(ServiceResponse.error(401, "Authentification requise. Veuillez vous connecter.")));
       }
       return false;
   }
}

Structure de la Base de Données

La base de données inclut une table dédiée à la gestion des jetons d'authentification, essentielle pour la sécurité de l'application. Cette table stocke les informations relatives aux jetons émis, leur utilisateur associé, le rôle et les dates de validité.


-- Structure de la table pour les jetons d'authentification
DROP TABLE IF EXISTS `auth_tokens`;
CREATE TABLE `auth_tokens` (
 `token_id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'Identifiant unique du jeton',
 `user_ref_id` BIGINT(20) NOT NULL COMMENT 'ID de l''utilisateur associé',
 `username_label` VARCHAR(100) NOT NULL COMMENT 'Nom d''utilisateur',
 `entity_context` VARCHAR(100) DEFAULT NULL COMMENT 'Contexte de l''entité (ex: table)',
 `user_role` VARCHAR(100) DEFAULT NULL COMMENT 'Rôle de l''utilisateur',
 `token_value` VARCHAR(200) NOT NULL COMMENT 'Valeur du jeton d''authentification',
 `creation_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Date de création du jeton',
 `expiry_date` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Date et heure d''expiration du jeton',
 PRIMARY KEY (`token_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='Table de gestion des jetons d''authentification';

-- Exemples de données pour la table auth_tokens
INSERT INTO `auth_tokens` VALUES (1, 101, 'voyageur_01', 'clients', 'Client', 'jwt_abcdef1234567890abcdef1234567890', '2023-04-01 10:30:00', '2023-04-01 12:30:00');
INSERT INTO `auth_tokens` VALUES (2, 201, 'agence_admin', 'agences', 'Administrateur', 'jwt_fedcba0987654321fedcba0987654321', '2023-04-01 11:00:00', '2023-04-01 13:00:00');
INSERT INTO `auth_tokens` VALUES (3, 102, 'explorer_xp', 'clients', 'Client', 'jwt_9876543210abcdef9876543210abcdef', '2023-04-01 11:45:00', '2023-04-01 13:45:00');
INSERT INTO `auth_tokens` VALUES (4, 1, 'system_super', 'system_users', 'SuperAdmin', 'jwt_zyxw7890zyxw7890zyxw7890zyxw7890', '2023-04-01 12:15:00', '2023-04-01 14:15:00');

Étiquettes: Java Spring Boot Vue.js MyBatis-Plus REST API

Publié le 23 juillet à 10h44