From ffbd8d220c74e9eed7f3eef1d48cbcf54ef4b7e4 Mon Sep 17 00:00:00 2001 From: Andrii Solianyk Date: Mon, 2 Jun 2025 13:46:09 +0200 Subject: [PATCH] =?UTF-8?q?du=C5=BCo=20lepsza=20autoryzacja=20teraz,=20du?= =?UTF-8?q?=C5=BCo=20lepsza.=20Tokeny=20wygasaj=C4=85,=20mo=C5=BCna=20mie?= =?UTF-8?q?=C4=87=20tylko=20jeden=20aktywny=20token=20per=20user?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/SecurityConfig.java | 1 - .../controller/AuthController.java | 5 ++++ .../security/JwtRequestFilter.java | 6 ++++- .../security/JwtUtil.java | 27 +++++++++++++++---- 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/main/java/_11/asktpk/artisanconnectbackend/config/SecurityConfig.java b/src/main/java/_11/asktpk/artisanconnectbackend/config/SecurityConfig.java index 34e57d6..bf88946 100644 --- a/src/main/java/_11/asktpk/artisanconnectbackend/config/SecurityConfig.java +++ b/src/main/java/_11/asktpk/artisanconnectbackend/config/SecurityConfig.java @@ -27,7 +27,6 @@ public class SecurityConfig { .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/v1/auth/**").permitAll() - .requestMatchers("/api/v1/admin/**").hasRole("ADMIN") .anyRequest().authenticated()) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); diff --git a/src/main/java/_11/asktpk/artisanconnectbackend/controller/AuthController.java b/src/main/java/_11/asktpk/artisanconnectbackend/controller/AuthController.java index 5f6d98d..230a964 100644 --- a/src/main/java/_11/asktpk/artisanconnectbackend/controller/AuthController.java +++ b/src/main/java/_11/asktpk/artisanconnectbackend/controller/AuthController.java @@ -5,6 +5,7 @@ import _11.asktpk.artisanconnectbackend.entities.Client; import _11.asktpk.artisanconnectbackend.security.JwtUtil; import _11.asktpk.artisanconnectbackend.service.ClientService; import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; @@ -12,6 +13,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +@Slf4j @RestController @RequestMapping("/api/v1/auth") public class AuthController { @@ -33,6 +35,7 @@ public class AuthController { String token = jwtUtil.generateToken(client.getEmail(), userRole, userId); + log.info("Logged in as " + client.getEmail()); return ResponseEntity.status(HttpStatus.OK) .body(new AuthResponseDTO(userId, userRole, token)); } else { @@ -54,6 +57,8 @@ public class AuthController { savedClient.getId() ); + log.info("Registered as " + savedClient.getEmail()); + return ResponseEntity.status(HttpStatus.CREATED) .body(new AuthResponseDTO( savedClient.getId(), diff --git a/src/main/java/_11/asktpk/artisanconnectbackend/security/JwtRequestFilter.java b/src/main/java/_11/asktpk/artisanconnectbackend/security/JwtRequestFilter.java index 7700ee2..458b719 100644 --- a/src/main/java/_11/asktpk/artisanconnectbackend/security/JwtRequestFilter.java +++ b/src/main/java/_11/asktpk/artisanconnectbackend/security/JwtRequestFilter.java @@ -36,8 +36,12 @@ public class JwtRequestFilter extends OncePerRequestFilter { if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { jwt = authorizationHeader.substring(7); - if (jwtUtil.isBlacklisted(jwt)) { + if (jwtUtil.isBlacklisted(jwt) || !jwtUtil.isLatestToken(jwt)) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.setContentType("application/json"); + response.setCharacterEncoding("UTF-8"); + String jsonResponse = "{\"error\": \"Token is invalid or expired. Please login again.\"}"; + response.getWriter().write(jsonResponse); return; } diff --git a/src/main/java/_11/asktpk/artisanconnectbackend/security/JwtUtil.java b/src/main/java/_11/asktpk/artisanconnectbackend/security/JwtUtil.java index 4ac4895..ae36e06 100644 --- a/src/main/java/_11/asktpk/artisanconnectbackend/security/JwtUtil.java +++ b/src/main/java/_11/asktpk/artisanconnectbackend/security/JwtUtil.java @@ -8,10 +8,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.crypto.SecretKey; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; @@ -40,11 +37,27 @@ public class JwtUtil { return Keys.hmacShaKeyFor(secret.getBytes()); } + private final Map userActiveTokens = new ConcurrentHashMap<>(); + + public boolean isLatestToken(String token) { + String email = extractEmail(token); + String tokenId = extractTokenId(token); + String latestTokenId = userActiveTokens.get(email); + + return latestTokenId != null && latestTokenId.equals(tokenId); + } + public String generateToken(String email, String role, Long userId) { Map claims = new HashMap<>(); claims.put("role", role); claims.put("userId", userId); - return createToken(claims, email); + claims.put("tokenId", UUID.randomUUID().toString()); + + String token = createToken(claims, email); + + userActiveTokens.put(email, extractTokenId(token)); + + return token; } private String createToken(Map claims, String subject) { @@ -57,6 +70,10 @@ public class JwtUtil { .compact(); } + public String extractTokenId(String token) { + return extractAllClaims(token).get("tokenId", String.class); + } + public String extractEmail(String token) { return extractClaim(token, Claims::getSubject); }