Système de gestion d'œuvres caritatives développé avec SpringBoot, Vue.js et MySQL

Le système de gestion d'œuvres caritatives est une application web full-stack construite autour de l'écosystème Java. Elle combine un backend SpringBoot avec un frontend Vue.js pour offrir une plateforme complète de gestion d'activités caritatives, incluant la gestion des bénévoles, des services, des forums et des actualités.

Architecture technique

Pile technologique backend :

  • SpringBoot comme framework principle
  • MyBatis pour la couche de persistance
  • MySQL comme base de données relationnelle
  • Maven pour la gestion des dépendances

Pile technologique frontend :

  • Vue.js comme framework JavaScript progressif
  • Element UI comme bibliothèque de composants
  • Axios pour les communications HTTP

Prérequis environnementaux

  • JDK 1.8 ou supérieur
  • MySQL version 5.7 minimum
  • Node.js 14 ou version ultérieure
  • IDE compatible Java (IntelliJ IDEA recommandé)

Rôles et fonctionnalités

Le système distingue deux types d'utilisateurs avec des permissions différentes :

Utilisateur standard :

  • Authentification et création de compte
  • Consultation des forums communautaires
  • Exploration des services caritatifs
  • Publication de commentaires sur les services
  • Lecture des actualités et informations
  • Gestion du profil personnel
  • Soumission de demandes de service

Administrateur :

  • Gestion des comptes utilisateurs
  • Administration du forum
  • Gestion du catalogue de services
  • Modération des commentaires
  • Traitement des demandes de service
  • Publication d'actualités et d'informations
  • Configuration des données de référence

Procédure de déploiemant

  1. Créer la base de données MySQL et importer le script SQL fourni
  2. Importer le projet back end dans l'IDE et exécuter mvn clean install
  3. Configurer les paramètres de connexion dans application.yml
  4. Démarrer l'application SpringBoot
  5. Accéder à l'interface frontend via le navigateur

Implémentation du contrôleur d'authentification

La gestion de l'authentification repose sur un contrôleur Spring MVC qui orchestre les opérations de connexion, d'inscription et de gestion des sessions utilisateurs.

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    @Autowired
    private AccountService accountService;

    @Autowired
    private SessionRegistry sessionRegistry;

    @PostMapping("/login")
    public ResponseEntity<ApiResponse> authenticateUser(@RequestBody LoginRequest request, 
                                                          HttpServletRequest httpRequest) {
        try {
            AccountProfile profile = accountService.verifyCredentials(
                request.getUsername(), 
                request.getPassword()
            );
            
            if (profile == null) {
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                    .body(new ApiResponse(false, "Identifiants invalides"));
            }
            
            HttpSession session = httpRequest.getSession(true);
            session.setAttribute("currentUser", profile);
            session.setMaxInactiveInterval(18000);
            
            LoginLogEntry logEntry = new LoginLogEntry();
            logEntry.setDisplayName(profile.getFullName() + " - " + profile.getUsername());
            logEntry.setTimestamp(LocalDateTime.now());
            logEntry.setIpAddress(httpRequest.getRemoteAddr());
            accountService.recordLoginEvent(logEntry);
            
            return ResponseEntity.ok(new ApiResponse(true, "Connexion réussie", profile));
        } catch (Exception ex) {
            log.error("Échec d'authentification", ex);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(new ApiResponse(false, "Erreur technique"));
        }
    }

    @PostMapping("/register")
    public ResponseEntity<ApiResponse> registerAccount(@RequestBody RegistrationRequest request,
                                                          HttpSession session) {
        try {
            String captchaCode = (String) session.getAttribute("captchaValue");
            if (!request.getCaptcha().equalsIgnoreCase(captchaCode)) {
                return ResponseEntity.badRequest()
                    .body(new ApiResponse(false, "Code de vérification incorrect"));
            }
            
            boolean isAvailable = accountService.checkUsernameAvailability(request.getUsername());
            if (!isAvailable) {
                return ResponseEntity.badRequest()
                    .body(new ApiResponse(false, "Nom d'utilisateur déjà pris"));
            }
            
            accountService.createNewAccount(request);
            return ResponseEntity.ok(new ApiResponse(true, "Inscription accomplie"));
        } catch (Exception ex) {
            log.error("Échec lors de l'inscription", ex);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(new ApiResponse(false, "Échec de l'inscription"));
        }
    }

    @GetMapping("/logout")
    public ResponseEntity<ApiResponse> terminateSession(HttpSession session) {
        session.invalidate();
        return ResponseEntity.ok(new ApiResponse(true, "Déconnexion effectuée"));
    }
}

Gestion des utilisateurs

Le contrôleur de gestion des utilisateurs permet l'administration complète des comptes : création, modification, suppression et réinitialisation des mots de passe.

@RestController
@RequestMapping("/api/users")
public class UserManagementController {

    @Autowired
    private UserManagementService userManagementService;

    @Autowired
    private FileStorageService fileStorageService;

    @PostMapping("/create")
    public ResponseEntity<ApiResponse> createUser(@RequestBody UserDto userDto) {
        try {
            userManagementService.persistNewUser(userDto);
            return ResponseEntity.ok(new ApiResponse(true, "Utilisateur créé avec succès"));
        } catch (Exception ex) {
            log.error("Erreur création utilisateur", ex);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(new ApiResponse(false, "Échec de la création"));
        }
    }

    @PutMapping("/update/{userId}")
    public ResponseEntity<ApiResponse> updateUserProfile(@PathVariable Long userId,
                                                           @RequestBody UserDto userDto) {
        try {
            String avatarPath = userDto.getAvatarPath();
            if (avatarPath != null && avatarPath.endsWith("_temp")) {
                String finalPath = fileStorageService.finalizeTempFile(avatarPath);
                userDto.setAvatarPath(finalPath);
                
                UserDto existingUser = userManagementService.fetchUserById(userId);
                if (existingUser != null && existingUser.getAvatarPath() != null) {
                    fileStorageService.deleteFile(existingUser.getAvatarPath());
                }
            }
            
            userManagementService.modifyUser(userId, userDto);
            return ResponseEntity.ok(new ApiResponse(true, "Profil mis à jour"));
        } catch (Exception ex) {
            log.error("Erreur mise à jour profil", ex);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(new ApiResponse(false, "Échec de la mise à jour"));
        }
    }

    @DeleteMapping("/remove/{userId}")
    public ResponseEntity<ApiResponse> removeUser(@PathVariable Long userId) {
        try {
            userManagementService.deleteUser(userId);
            return ResponseEntity.ok(new ApiResponse(true, "Suppression réussie"));
        } catch (Exception ex) {
            log.error("Erreur suppression utilisateur", ex);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(new ApiResponse(false, "Échec de la suppression"));
        }
    }

    @PostMapping("/reset-password/{userId}")
    public ResponseEntity<ApiResponse> resetPassword(@PathVariable Long userId) {
        try {
            userManagementService.resetUserPassword(userId);
            return ResponseEntity.ok(new ApiResponse(true, "Mot de passe réinitialisé"));
        } catch (Exception ex) {
            log.error("Erreur réinitialisation mot de passe", ex);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(new ApiResponse(false, "Échec de la réinitialisation"));
        }
    }

    @GetMapping("/check-username")
    public ResponseEntity<Boolean> verifyUsernameAvailability(@RequestParam String username) {
        boolean isAvailable = userManagementService.checkUsernameAvailability(username);
        return ResponseEntity.ok(!isAvailable);
    }
}

Gestion des rôles et permissions

L'attribution des menus aux rôles s'effectue via une structure arborescente représentée en JSON, permettant une gestion granulaire des droits d'accès.

@RestController
@RequestMapping("/api/roles")
public class RolePermissionController {

    @Autowired
    private RolePermissionService rolePermissionService;

    @GetMapping("/{roleId}/menu-tree")
    public ResponseEntity<MenuTreeResponse> fetchRoleMenuTree(@PathVariable Integer roleId) {
        MenuTreeResponse treeData = rolePermissionService.buildMenuTreeForRole(roleId);
        return ResponseEntity.ok(treeData);
    }

    @DeleteMapping("/remove/{roleId}")
    public ResponseEntity<ApiResponse> deleteRole(@PathVariable Integer roleId) {
        try {
            rolePermissionService.removeRole(roleId);
            return ResponseEntity.ok(new ApiResponse(true, "Rôle supprimé"));
        } catch (Exception ex) {
            log.error("Erreur suppression rôle", ex);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(new ApiResponse(false, "Échec de la suppression du rôle"));
        }
    }
}

Modification du mot de passe utilisateur

Le changement de mot de passe utilise un hachage MD5 et vérifie l'identité de l'utilisateur via la session active.

@RestController
@RequestMapping("/api/security")
public class PasswordUpdateController {

    @Autowired
    private UserManagementService userManagementService;

    @PostMapping("/change-password")
    public ResponseEntity<ApiResponse> changePassword(@RequestBody PasswordChangeRequest request,
                                                        HttpSession session) {
        try {
            AccountProfile currentUser = (AccountProfile) session.getAttribute("currentUser");
            if (currentUser == null) {
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                    .body(new ApiResponse(false, "Session expirée"));
            }
            
            String hashedPassword = DigestUtils.md5DigestAsHex(
                request.getNewPassword().getBytes(StandardCharsets.UTF_8)
            );
            
            PasswordUpdateDto updateDto = new PasswordUpdateDto();
            updateDto.setUserId(currentUser.getId());
            updateDto.setNewPasswordHash(hashedPassword);
            
            userManagementService.updatePassword(updateDto);
            return ResponseEntity.ok(new ApiResponse(true, "Mot de passe modifié"));
        } catch (Exception ex) {
            log.error("Erreur changement mot de passe", ex);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(new ApiResponse(false, "Échec du changement de mot de passe"));
        }
    }
}

Génération du captcha

La génération de codes de vérification graphiques utilise l'API BufferedImage pour produire des images de sécurité anti-robot.

@RestController
@RequestMapping("/api/captcha")
public class CaptchaController {

    @GetMapping("/generate")
    public void generateCaptcha(HttpServletResponse response, HttpSession session) throws IOException {
        int imageWidth = 120;
        int imageHeight = 40;
        
        BufferedImage captchaImage = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = captchaImage.createGraphics();
        
        graphics.setColor(Color.LIGHT_GRAY);
        graphics.fillRect(0, 0, imageWidth, imageHeight);
        
        String verificationCode = generateRandomCode(4);
        session.setAttribute("captchaValue", verificationCode);
        
        graphics.setColor(Color.DARK_GRAY);
        graphics.setFont(new Font("Arial", Font.BOLD, 24));
        graphics.drawString(verificationCode, 30, 30);
        
        response.setContentType("image/png");
        try (ServletOutputStream outputStream = response.getOutputStream()) {
            ImageIO.write(captchaImage, "png", outputStream);
        }
    }

    private String generateRandomCode(int length) {
        String characters = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
        StringBuilder builder = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            builder.append(characters.charAt(random.nextInt(characters.length())));
        }
        return builder.toString();
    }
}

Interfaces principales

Le frontend expose deux points d'accès distincts :

  • Interface publique : accessible à l'adresse /front/index.html, elle permet aux visiteurs de consulter les services, forums et actualités
  • Interface d'administration : accessible via /admin/dist/index.html, elle offre le tableau de bord de gestion complète

Étiquettes: SpringBoot Vue.js MyBatis MySQL ElementUI

Publié le 24 juillet à 20h06