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); }