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');