comment delete et limite de 5 requete post toute les minutes
This commit is contained in:
parent
f41c830970
commit
40da59bd31
@ -1,4 +1,5 @@
|
|||||||
package com.guams.review.configuration;
|
package com.guams.review.configuration;
|
||||||
|
|
||||||
import com.guams.review.service.AuthorService;
|
import com.guams.review.service.AuthorService;
|
||||||
import io.jsonwebtoken.JwtException;
|
import io.jsonwebtoken.JwtException;
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
@ -11,7 +12,11 @@ import org.springframework.security.core.userdetails.UserDetails;
|
|||||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||||
@ -19,6 +24,13 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
private final JwtTokenUtil jwtTokenUtil;
|
private final JwtTokenUtil jwtTokenUtil;
|
||||||
private final AuthorService authorService;
|
private final AuthorService authorService;
|
||||||
|
|
||||||
|
// Map pour traquer les requêtes : UUID -> { dernière réinitialisation, compteur de requêtes }
|
||||||
|
private final ConcurrentHashMap<String, RateLimiter> rateLimiters = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
// Configuration du rate limiting
|
||||||
|
private static final int MAX_REQUESTS = 5;
|
||||||
|
private static final long TIME_WINDOW_MS = 60 * 1000; // 1 minute
|
||||||
|
|
||||||
public JwtAuthenticationFilter(JwtTokenUtil jwtTokenUtil, AuthorService authorService) {
|
public JwtAuthenticationFilter(JwtTokenUtil jwtTokenUtil, AuthorService authorService) {
|
||||||
this.jwtTokenUtil = jwtTokenUtil;
|
this.jwtTokenUtil = jwtTokenUtil;
|
||||||
this.authorService = authorService;
|
this.authorService = authorService;
|
||||||
@ -35,9 +47,16 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
String authorUuid = jwtTokenUtil.extractAuthorId(token); // Récupère l'UUID du token
|
String authorUuid = jwtTokenUtil.extractAuthorId(token); // Récupère l'UUID du token
|
||||||
|
|
||||||
if (authorUuid != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
if (authorUuid != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||||
UserDetails userDetails = authorService.loadUserByUsername(authorUuid); // Utilise le service pour charger l'auteur par UUID
|
UserDetails userDetails = authorService.loadUserByUsername(authorUuid); // Charge l'auteur
|
||||||
|
|
||||||
if (jwtTokenUtil.validateToken(token, userDetails, authorUuid)) {
|
if (jwtTokenUtil.validateToken(token, userDetails, authorUuid)) {
|
||||||
|
// Applique le rate limiting
|
||||||
|
if (isRateLimited(authorUuid, request)) {
|
||||||
|
response.setStatus(429); // TOO MANY REQUESTS
|
||||||
|
response.getWriter().write("Rate limit exceeded. Please try again later.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
UsernamePasswordAuthenticationToken authentication =
|
UsernamePasswordAuthenticationToken authentication =
|
||||||
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
|
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
|
||||||
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||||
@ -50,7 +69,42 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
filterChain.doFilter(request, response); // Continue le traitement
|
filterChain.doFilter(request, response); // Continue le traitement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isRateLimited(String authorUuid, HttpServletRequest request) {
|
||||||
|
// Ne limite que les requêtes POST
|
||||||
|
if (!"POST".equalsIgnoreCase(request.getMethod())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Récupère ou crée un RateLimiter pour l'utilisateur
|
||||||
|
RateLimiter limiter = rateLimiters.computeIfAbsent(authorUuid, k -> new RateLimiter());
|
||||||
|
|
||||||
|
synchronized (limiter) {
|
||||||
|
long now = Instant.now().toEpochMilli();
|
||||||
|
if (now - limiter.lastReset > TIME_WINDOW_MS) {
|
||||||
|
// Réinitialise la fenêtre de temps et le compteur
|
||||||
|
limiter.lastReset = now;
|
||||||
|
limiter.requestCount.set(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (limiter.requestCount.incrementAndGet() > MAX_REQUESTS) {
|
||||||
|
return true; // Limite atteinte
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Classe interne pour suivre les requêtes
|
||||||
|
private static class RateLimiter {
|
||||||
|
private long lastReset;
|
||||||
|
private AtomicInteger requestCount;
|
||||||
|
|
||||||
|
public RateLimiter() {
|
||||||
|
this.lastReset = Instant.now().toEpochMilli();
|
||||||
|
this.requestCount = new AtomicInteger(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,12 @@ public class AuthorController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @PutMapping("/{id}/password")
|
||||||
|
// public void changePassword(@PathVariable UUID id, @RequestBody Author updatedAuthor, Authentication authentication) {
|
||||||
|
// Author authorToUpdate = authorService.verifyIfUserIsAuthorized(authentication, id);
|
||||||
|
// if (passwordEncoder.matches(updatedAuthor.getPassword(), authorToUpdate.getPassword())) {}
|
||||||
|
// }
|
||||||
|
|
||||||
@PutMapping(value = "{id}/avatar", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
|
@PutMapping(value = "{id}/avatar", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
|
||||||
public Author updateUserAvatar(@PathVariable UUID id, @RequestPart MultipartFile avatar, Authentication authentication) throws IOException {
|
public Author updateUserAvatar(@PathVariable UUID id, @RequestPart MultipartFile avatar, Authentication authentication) throws IOException {
|
||||||
Author authorToUpdate = authorService.verifyIfUserIsAuthorized(authentication, id);
|
Author authorToUpdate = authorService.verifyIfUserIsAuthorized(authentication, id);
|
||||||
|
@ -85,7 +85,7 @@ public class CommentController {
|
|||||||
throw new UnauthorizedExecption("You are not authorized to access this resource");
|
throw new UnauthorizedExecption("You are not authorized to access this resource");
|
||||||
}
|
}
|
||||||
Comment commentToDelete = commentService.findById(id).orElseThrow(() -> new NotFoundException("Comment not found"));
|
Comment commentToDelete = commentService.findById(id).orElseThrow(() -> new NotFoundException("Comment not found"));
|
||||||
commentService.delete(commentToDelete);
|
|
||||||
commentService.deleteAssociationByCommentId(commentToDelete.getId());
|
commentService.deleteAssociationByCommentId(commentToDelete.getId());
|
||||||
|
commentService.delete(commentToDelete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,14 @@ public class PostController {
|
|||||||
Author authenticatedAuthor = authorService.findByName(authentication.getName()).orElseThrow(() -> new NotFoundException("Author not found"));
|
Author authenticatedAuthor = authorService.findByName(authentication.getName()).orElseThrow(() -> new NotFoundException("Author not found"));
|
||||||
if (authorService.listPublicationOfAuthor(authenticatedAuthor.getId()).stream().map(Post::getId).toList().contains(id)) {
|
if (authorService.listPublicationOfAuthor(authenticatedAuthor.getId()).stream().map(Post::getId).toList().contains(id)) {
|
||||||
Post postToDelete = postService.findById(id).orElseThrow(() -> new NotFoundException("Post not found"));
|
Post postToDelete = postService.findById(id).orElseThrow(() -> new NotFoundException("Post not found"));
|
||||||
commentService.getCommentsByPostId(id).stream().map(mapper::mapCommentWithAuthor).forEach(commentService::delete);
|
commentService.getCommentsByPostId(id)
|
||||||
|
.stream()
|
||||||
|
.map(mapper::mapCommentWithAuthor)
|
||||||
|
.forEach(comment -> commentService.deleteAssociationByCommentId(comment.getId()));
|
||||||
|
commentService.getCommentsByPostId(id)
|
||||||
|
.stream()
|
||||||
|
.map(mapper::mapCommentWithAuthor)
|
||||||
|
.forEach(commentService::delete);
|
||||||
postService.delete(authenticatedAuthor.getId(), postToDelete.getId());
|
postService.delete(authenticatedAuthor.getId(), postToDelete.getId());
|
||||||
} else {
|
} else {
|
||||||
throw new UnauthorizedExecption("You do not have permission to delete this post");
|
throw new UnauthorizedExecption("You do not have permission to delete this post");
|
||||||
|
Loading…
Reference in New Issue
Block a user