diff --git a/linktreeclone-backend/src/main/java/br/com/linktreeclone/exception/RestExceptionHandler.java b/linktreeclone-backend/src/main/java/br/com/linktreeclone/exception/RestExceptionHandler.java index 8782ca9..8d6a13b 100644 --- a/linktreeclone-backend/src/main/java/br/com/linktreeclone/exception/RestExceptionHandler.java +++ b/linktreeclone-backend/src/main/java/br/com/linktreeclone/exception/RestExceptionHandler.java @@ -13,6 +13,18 @@ public class RestExceptionHandler { public record ErrorResponse(LocalDateTime timestamp, int status, String error, String message, String path) {} + @ExceptionHandler(InvalidTokenException.class) + public ResponseEntity handleInvalidTokenException(InvalidTokenException ex, WebRequest request) { + ErrorResponse errorResponse = new ErrorResponse( + LocalDateTime.now(), + HttpStatus.FORBIDDEN.value(), + "Forbidden", + ex.getMessage(), + request.getDescription(false).replace("uri=", "") + ); + return new ResponseEntity<>(errorResponse, HttpStatus.FORBIDDEN); + } + @ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity handleResourceNotFoundException(ResourceNotFoundException ex, WebRequest request) { ErrorResponse errorResponse = new ErrorResponse( diff --git a/linktreeclone-backend/src/main/java/br/com/linktreeclone/security/SecurityFilter.java b/linktreeclone-backend/src/main/java/br/com/linktreeclone/security/SecurityFilter.java index 8800880..cb9cf41 100644 --- a/linktreeclone-backend/src/main/java/br/com/linktreeclone/security/SecurityFilter.java +++ b/linktreeclone-backend/src/main/java/br/com/linktreeclone/security/SecurityFilter.java @@ -1,16 +1,20 @@ package br.com.linktreeclone.security; import br.com.linktreeclone.repository.UserRepository; +import br.com.linktreeclone.exception.InvalidTokenException; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.servlet.HandlerExceptionResolver; import java.io.IOException; import java.util.UUID; @@ -24,24 +28,37 @@ public class SecurityFilter extends OncePerRequestFilter @Autowired private UserRepository userRepository; + @Autowired + @Qualifier("handlerExceptionResolver") + private HandlerExceptionResolver resolver; + @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = this.recoverToken(request); - if (token != null) { - String subject = tokenService.validateToken(token); - UUID userId = UUID.fromString(subject); + try + { + if (token != null) + { + String subject = tokenService.validateToken(token); + UUID userId = UUID.fromString(subject); + + UserDetails user = userRepository.findById(userId) + .orElseThrow(() -> new UsernameNotFoundException("Usuário não encontrado com o ID: " + userId)); - UserDetails user = userRepository.findById(userId) - .orElseThrow(() -> new RuntimeException("User Not Found")); + var authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + filterChain.doFilter(request, response); - var authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()); - SecurityContextHolder.getContext().setAuthentication(authentication); } - filterChain.doFilter(request, response); + catch (Exception e) + { + resolver.resolveException(request, response, null, e); + } } private String recoverToken(HttpServletRequest request) diff --git a/linktreeclone-backend/src/main/java/br/com/linktreeclone/security/TokenService.java b/linktreeclone-backend/src/main/java/br/com/linktreeclone/security/TokenService.java index 0a59aec..5119105 100644 --- a/linktreeclone-backend/src/main/java/br/com/linktreeclone/security/TokenService.java +++ b/linktreeclone-backend/src/main/java/br/com/linktreeclone/security/TokenService.java @@ -8,6 +8,12 @@ import jakarta.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.MalformedJwtException; +import io.jsonwebtoken.UnsupportedJwtException; +import io.jsonwebtoken.security.SignatureException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.crypto.SecretKey; import java.nio.charset.StandardCharsets; @@ -24,6 +30,8 @@ public class TokenService private SecretKey key; + private static final Logger logger = LoggerFactory.getLogger(TokenService.class); + @PostConstruct public void init() { this.key = Keys.hmacShaKeyFor(jwtSecretString.getBytes(StandardCharsets.UTF_8)); @@ -53,8 +61,25 @@ public String validateToken(String token) .getBody() .getSubject(); } - catch (Exception e) { - throw new InvalidTokenException("Token JWT expirado ou inválido"); + catch (ExpiredJwtException ex) { + logger.error("Token JWT expirou: {}", ex.getMessage()); + throw new InvalidTokenException("Sua sessão expirou. Por favor, faça login novamente."); + } + catch (SignatureException ex) { + logger.error("Assinatura do JWT é inválida: {}", ex.getMessage()); + throw new InvalidTokenException("Assinatura do token é inválida."); + } + catch (MalformedJwtException ex) { + logger.error("Token JWT malformado: {}", ex.getMessage()); + throw new InvalidTokenException("Token malformado."); + } + catch (UnsupportedJwtException ex) { + logger.error("Token JWT não é suportado: {}", ex.getMessage()); + throw new InvalidTokenException("Este tipo de token não é suportado."); + } + catch (IllegalArgumentException ex) { + logger.error("O conteúdo do JWT está vazio: {}", ex.getMessage()); + throw new InvalidTokenException("Token inválido ou vazio."); } } } \ No newline at end of file