JwtFilter.java
package com.archiweb.security;
import com.archiweb.repository.UserRepository;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@Component
public class JwtFilter extends OncePerRequestFilter {
private final JwtService jwtService;
private final UserRepository userRepository;
public JwtFilter(JwtService jwtService, UserRepository userRepository) {
this.jwtService = jwtService;
this.userRepository = userRepository;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain)
throws ServletException, IOException {
try {
String path = request.getServletPath() != null ? request.getServletPath() : "";
String requestURI = request.getRequestURI() != null ? request.getRequestURI() : "";
// Log toutes les requêtes newsletter pour debug
if (path.contains("/newsletter") || (requestURI != null && requestURI.contains("/newsletter"))) {
System.out.println("🔍 JwtFilter - Requête newsletter reçue: path=" + path + ", URI=" + requestURI + " | Method: " + request.getMethod());
System.out.println("🔍 JwtFilter - Authorization header IMMEDIAT: " + (request.getHeader("Authorization") != null ? "Présent" : "Absent"));
}
// ✅ Routes publiques : on ignore le JWT
// IMPORTANT: Pour /api/newsletter/subscribe, on doit faire une comparaison exacte ou avec un slash après
// car /api/newsletter/subscribers/count commence aussi par /api/newsletter/subscribe !
boolean isPublicRoute = path.startsWith("/api/auth")
|| path.startsWith("/swagger")
|| path.startsWith("/v3")
|| path.startsWith("/api/rgpd")
|| path.startsWith("/api/contact/send")
|| path.equals("/api/newsletter/subscribe") // Comparaison exacte pour éviter le match avec /subscribers/count
|| path.startsWith("/api/settings")
|| path.startsWith("/actuator");
if (path.contains("/newsletter")) {
System.out.println("🔍 JwtFilter - Check route publique pour: " + path + " -> " + isPublicRoute);
if (path.equals("/api/newsletter/subscribe")) {
System.out.println("🔍 JwtFilter - MATCH EXACT avec /api/newsletter/subscribe: " + path.equals("/api/newsletter/subscribe"));
}
}
if (isPublicRoute) {
if (path.contains("/newsletter")) {
System.out.println("🔍 JwtFilter - Route publique newsletter détectée, on ignore JWT: " + path);
}
chain.doFilter(request, response);
return;
}
if (path.contains("/newsletter")) {
System.out.println("🔍 JwtFilter - Route newsletter PROTÉGÉE, on continue la validation: " + path);
}
// ✅ Vérifie la présence du header Authorization
final String authHeader = request.getHeader("Authorization");
if (path.contains("/newsletter")) {
System.out.println("🔍 JwtFilter - Authorization header: " + (authHeader != null ? "Présent" : "Absent"));
}
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
if (path.contains("/newsletter")) {
System.out.println("❌ JwtFilter - Pas de token JWT valide pour: " + path);
}
chain.doFilter(request, response);
return;
}
// ✅ Extraction du token
String token = authHeader.substring(7);
if (path.contains("/newsletter")) {
System.out.println("🔍 JwtFilter - Validation du token pour: " + path);
System.out.println("🔍 JwtFilter - Token (premiers 20 chars): " + (token.length() > 20 ? token.substring(0, 20) + "..." : token));
}
boolean isValid = jwtService.validateToken(token);
if (path.contains("/newsletter")) {
System.out.println("🔍 JwtFilter - Résultat validation: " + isValid);
}
if (!isValid) {
if (path.contains("/newsletter")) {
System.out.println("❌ JwtFilter - Token invalide pour: " + path);
}
chain.doFilter(request, response);
return;
}
if (path.contains("/newsletter")) {
System.out.println("✅ JwtFilter - Token valide pour: " + path);
}
// ✅ Extraction de l'email depuis le token
String email = jwtService.extractEmail(token);
String tokenRole = jwtService.extractRole(token);
// ✅ Recherche de l'utilisateur et définition du contexte de sécurité
userRepository.findByEmail(email).ifPresentOrElse(user -> {
// 🔹 Création d'un UserDetails avec ses rôles
// Pour les utilisateurs OAuth (sans mot de passe), on utilise un placeholder
// car Spring Security nécessite un password non-null, mais il ne sera jamais utilisé
String password = user.getPassword() != null ? user.getPassword() : "{noop}OAUTH_USER_NO_PASSWORD";
// Extraire le rôle de l'utilisateur depuis la base de données (source de vérité)
// Le rôle dans le token peut être obsolète, on utilise toujours celui de la DB
String roleName = user.getRole() != null ? user.getRole().getName() : "ROLE_USER";
UserDetails userDetails = org.springframework.security.core.userdetails.User
.withUsername(user.getEmail())
.password(password)
.authorities(roleName) // exemple : ROLE_ADMIN
.build();
// Log pour débogage des problèmes de permissions
if (path.contains("/admin/") || path.contains("/wins/home-delivery") || path.contains("/stats/") || path.contains("/contact/messages") || path.contains("/newsletter/")) {
System.out.println("🔍 DEBUG JwtFilter - Path: " + path);
System.out.println("🔍 DEBUG JwtFilter - User: " + email + ", DB Role: " + roleName + ", Token Role: " + tokenRole);
System.out.println("🔍 DEBUG JwtFilter - Authorities: " + userDetails.getAuthorities());
if (path.contains("/contact/messages")) {
System.out.println("✅ DEBUG JwtFilter - Route contact/messages détectée, autorités: " + userDetails.getAuthorities());
}
if (path.contains("/newsletter/")) {
System.out.println("✅ DEBUG JwtFilter - Route newsletter détectée, autorités: " + userDetails.getAuthorities());
}
}
// 🔹 Injection dans le contexte Spring Security
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}, () -> {
// Si l'utilisateur n'est pas trouvé, on log pour débogage
System.out.println("DEBUG JwtFilter - Utilisateur non trouvé pour email: " + email);
});
if (path.contains("/newsletter")) {
System.out.println("✅ JwtFilter - Fin du traitement pour: " + path + ", on passe à la chaîne suivante");
}
// ✅ Poursuite du filtre
chain.doFilter(request, response);
} catch (Exception e) {
String path = request.getServletPath() != null ? request.getServletPath() : "";
System.err.println("❌ JwtFilter - Exception pour: " + path + " -> " + e.getMessage());
e.printStackTrace();
throw e;
}
}
}