diff --git a/pom.xml b/pom.xml index e61d69e..c664f71 100644 --- a/pom.xml +++ b/pom.xml @@ -172,6 +172,11 @@ + + com.github.ben-manes.caffeine + caffeine + 3.1.8 + diff --git a/src/main/java/com/skycrate/backend/skycrateBackend/controller/AuthController.java b/src/main/java/com/skycrate/backend/skycrateBackend/controller/AuthController.java index b823bd4..55d017b 100644 --- a/src/main/java/com/skycrate/backend/skycrateBackend/controller/AuthController.java +++ b/src/main/java/com/skycrate/backend/skycrateBackend/controller/AuthController.java @@ -24,12 +24,26 @@ public class AuthController { } @PostMapping("/login") - public ResponseEntity login(@RequestBody LoginRequest request) { - authManager.authenticate(new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword())); + public ResponseEntity login(@RequestBody LoginRequest request, HttpServletRequest servletRequest) { + String ip = servletRequest.getRemoteAddr(); // or use request.getEmail() as key + + if (rateLimiterService.isBlocked(ip)) { + return ResponseEntity.status(429).body("Too many login attempts. Please try again later."); + } + + try { + authManager.authenticate( + new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword()) + ); + } catch (Exception ex) { + rateLimiterService.recordFailedAttempt(ip); + return ResponseEntity.status(401).body("Invalid credentials."); + } User user = userRepository.findByEmail(request.getEmail()) .orElseThrow(() -> new RuntimeException("User not found")); + rateLimiterService.resetAttempts(ip); String token = jwtService.generateToken(user); return ResponseEntity.ok().body(token); } diff --git a/src/main/java/com/skycrate/backend/skycrateBackend/security/RateLimiterService.java b/src/main/java/com/skycrate/backend/skycrateBackend/security/RateLimiterService.java new file mode 100644 index 0000000..7aad7ee --- /dev/null +++ b/src/main/java/com/skycrate/backend/skycrateBackend/security/RateLimiterService.java @@ -0,0 +1,35 @@ +package com.skycrate.backend.skycrateBackend.security; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import org.springframework.stereotype.Service; + +import java.util.concurrent.TimeUnit; + +@Service +public class RateLimiterService { + + private final Cache attemptsCache; + + private static final int MAX_ATTEMPTS = 5; + + public RateLimiterService() { + this.attemptsCache = Caffeine.newBuilder() + .expireAfterWrite(1, TimeUnit.MINUTES) + .build(); + } + + public boolean isBlocked(String key) { + Integer attempts = attemptsCache.getIfPresent(key); + return attempts != null && attempts >= MAX_ATTEMPTS; + } + + public void recordFailedAttempt(String key) { + int attempts = attemptsCache.getIfPresent(key) == null ? 0 : attemptsCache.getIfPresent(key); + attemptsCache.put(key, attempts + 1); + } + + public void resetAttempts(String key) { + attemptsCache.invalidate(key); + } +} \ No newline at end of file