diff --git a/rag-service/src/main/java/com/balex/rag/model/entity/RefreshToken.java b/rag-service/src/main/java/com/balex/rag/model/entity/RefreshToken.java index 69298e1..c15f9af 100644 --- a/rag-service/src/main/java/com/balex/rag/model/entity/RefreshToken.java +++ b/rag-service/src/main/java/com/balex/rag/model/entity/RefreshToken.java @@ -22,9 +22,10 @@ public class RefreshToken { @Column(nullable = false) private LocalDateTime created; + @Column(name = "session_id") + private String sessionId; + @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) @JoinColumn(name = "user_id", nullable = false) private User user; - -} - +} \ No newline at end of file diff --git a/rag-service/src/main/java/com/balex/rag/repo/RefreshTokenRepository.java b/rag-service/src/main/java/com/balex/rag/repo/RefreshTokenRepository.java index 9ba5031..f90d1f1 100644 --- a/rag-service/src/main/java/com/balex/rag/repo/RefreshTokenRepository.java +++ b/rag-service/src/main/java/com/balex/rag/repo/RefreshTokenRepository.java @@ -11,5 +11,5 @@ public interface RefreshTokenRepository extends JpaRepository findByUserId(Integer userId); -} - + Optional findByUserEmail(String email); +} \ No newline at end of file diff --git a/rag-service/src/main/java/com/balex/rag/security/JwtTokenProvider.java b/rag-service/src/main/java/com/balex/rag/security/JwtTokenProvider.java index 8d1c2c4..ffb0129 100644 --- a/rag-service/src/main/java/com/balex/rag/security/JwtTokenProvider.java +++ b/rag-service/src/main/java/com/balex/rag/security/JwtTokenProvider.java @@ -28,12 +28,13 @@ public class JwtTokenProvider { this.jwtValidityInMilliseconds = jwtValidityInMilliseconds; } - public String generateToken(@NonNull User user) { + public String generateToken(@NonNull User user, String sessionId) { Map claims = new HashMap<>(); claims.put(AuthenticationConstants.USER_ID, user.getId()); claims.put(AuthenticationConstants.USERNAME, user.getUsername()); claims.put(AuthenticationConstants.USER_EMAIL, user.getEmail()); claims.put(AuthenticationConstants.USER_REGISTRATION_STATUS, user.getRegistrationStatus().name()); + claims.put(AuthenticationConstants.SESSION_ID, sessionId); claims.put(AuthenticationConstants.LAST_UPDATE, LocalDateTime.now().toString()); return createToken(claims, user.getEmail()); @@ -93,5 +94,11 @@ public class JwtTokenProvider { .signWith(secretKey, SignatureAlgorithm.HS512) .compact(); } + + public String getSessionId(String token) { + Claims claims = getAllClaimsFromToken(token); + return claims.get(AuthenticationConstants.SESSION_ID, String.class); + } + } diff --git a/rag-service/src/main/java/com/balex/rag/security/filter/JwtRequestFilter.java b/rag-service/src/main/java/com/balex/rag/security/filter/JwtRequestFilter.java index 2d8b5b7..dee0f33 100644 --- a/rag-service/src/main/java/com/balex/rag/security/filter/JwtRequestFilter.java +++ b/rag-service/src/main/java/com/balex/rag/security/filter/JwtRequestFilter.java @@ -2,6 +2,7 @@ package com.balex.rag.security.filter; import com.balex.rag.model.constants.ApiErrorMessage; import com.balex.rag.security.JwtTokenProvider; +import com.balex.rag.service.impl.RefreshTokenServiceImpl; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.MalformedJwtException; import io.jsonwebtoken.SignatureException; @@ -36,6 +37,7 @@ public class JwtRequestFilter extends OncePerRequestFilter { private static final String REGISTER_PATH = "/auth/register"; private final JwtTokenProvider jwtTokenProvider; + private final RefreshTokenServiceImpl refreshTokenService; @Override protected void doFilterInternal( @@ -61,8 +63,16 @@ public class JwtRequestFilter extends OncePerRequestFilter { Optional emailOpt = Optional.ofNullable(jwtTokenProvider.getUsername(jwt)); Optional userIdOpt = Optional.ofNullable(jwtTokenProvider.getUserId(jwt)); + String sessionId = jwtTokenProvider.getSessionId(jwt); if (emailOpt.isPresent() && userIdOpt.isPresent()) { + // Check session validity + Optional activeSessionId = refreshTokenService.getSessionIdByEmail(emailOpt.get()); + if (activeSessionId.isEmpty() || !activeSessionId.get().equals(sessionId)) { + sendErrorResponse(response, HttpStatus.UNAUTHORIZED, "SESSION_INVALIDATED"); + return; + } + if (SecurityContextHolder.getContext().getAuthentication() == null) { List authorities = Collections.singletonList(new SimpleGrantedAuthority(USER_ROLE)); @@ -75,7 +85,6 @@ public class JwtRequestFilter extends OncePerRequestFilter { } } - } catch (ExpiredJwtException e) { handleTokenExpiration(requestURI, jwt, response); return; diff --git a/rag-service/src/main/java/com/balex/rag/service/impl/AuthServiceImpl.java b/rag-service/src/main/java/com/balex/rag/service/impl/AuthServiceImpl.java index baf93df..cc04ba9 100644 --- a/rag-service/src/main/java/com/balex/rag/service/impl/AuthServiceImpl.java +++ b/rag-service/src/main/java/com/balex/rag/service/impl/AuthServiceImpl.java @@ -53,7 +53,7 @@ public class AuthServiceImpl implements AuthService { .orElseThrow(() -> new InvalidDataException(ApiErrorMessage.INVALID_USER_OR_PASSWORD.getMessage())); RefreshToken refreshToken = refreshTokenService.generateOrUpdateRefreshToken(user); - String token = jwtTokenProvider.generateToken(user); + String token = jwtTokenProvider.generateToken(user, refreshToken.getSessionId()); UserProfileDTO userProfileDTO = userMapper.toUserProfileDto(user, token, refreshToken.getToken()); userProfileDTO.setToken(token); @@ -65,7 +65,7 @@ public class AuthServiceImpl implements AuthService { RefreshToken refreshToken = refreshTokenService.validateAndRefreshToken(refreshTokenValue); User user = refreshToken.getUser(); - String accessToken = jwtTokenProvider.generateToken(user); + String accessToken = jwtTokenProvider.generateToken(user, refreshToken.getSessionId()); return RagResponse.createSuccessfulWithNewToken( userMapper.toUserProfileDto(user, accessToken, refreshToken.getToken()) @@ -74,7 +74,6 @@ public class AuthServiceImpl implements AuthService { @Override public RagResponse registerUser(@NotNull RegistrationUserRequest request) { - accessValidator.validateNewUser( request.getUsername(), request.getEmail(), @@ -86,8 +85,8 @@ public class AuthServiceImpl implements AuthService { newUser.setPassword(passwordEncoder.encode(request.getPassword())); userRepository.save(newUser); - RefreshToken refreshToken = refreshTokenService.generateOrUpdateRefreshToken(newUser); - String token = jwtTokenProvider.generateToken(newUser); + RefreshToken refreshToken = refreshTokenService.generateOrUpdateRefreshToken(newUser); + String token = jwtTokenProvider.generateToken(newUser, refreshToken.getSessionId()); UserProfileDTO userProfileDTO = userMapper.toUserProfileDto(newUser, token, refreshToken.getToken()); userProfileDTO.setToken(token); diff --git a/rag-service/src/main/java/com/balex/rag/service/impl/RefreshTokenServiceImpl.java b/rag-service/src/main/java/com/balex/rag/service/impl/RefreshTokenServiceImpl.java index 3cd536b..f5fb107 100644 --- a/rag-service/src/main/java/com/balex/rag/service/impl/RefreshTokenServiceImpl.java +++ b/rag-service/src/main/java/com/balex/rag/service/impl/RefreshTokenServiceImpl.java @@ -12,6 +12,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.time.LocalDateTime; +import java.util.Optional; @Slf4j @Service @@ -21,10 +22,12 @@ public class RefreshTokenServiceImpl implements RefreshTokenService { @Override public RefreshToken generateOrUpdateRefreshToken(User user) { + String sessionId = ApiUtils.generateUuidWithoutDash(); return refreshTokenRepository.findByUserId(user.getId()) .map(refreshToken -> { refreshToken.setCreated(LocalDateTime.now()); refreshToken.setToken(ApiUtils.generateUuidWithoutDash()); + refreshToken.setSessionId(sessionId); return refreshTokenRepository.save(refreshToken); }) .orElseGet(() -> { @@ -32,6 +35,7 @@ public class RefreshTokenServiceImpl implements RefreshTokenService { newToken.setUser(user); newToken.setCreated(LocalDateTime.now()); newToken.setToken(ApiUtils.generateUuidWithoutDash()); + newToken.setSessionId(sessionId); return refreshTokenRepository.save(newToken); }); } @@ -46,5 +50,8 @@ public class RefreshTokenServiceImpl implements RefreshTokenService { return refreshTokenRepository.save(refreshToken); } -} - + public Optional getSessionIdByEmail(String email) { + return refreshTokenRepository.findByUserEmail(email) + .map(RefreshToken::getSessionId); + } +} \ No newline at end of file diff --git a/rag-service/src/main/java/com/balex/rag/service/model/AuthenticationConstants.java b/rag-service/src/main/java/com/balex/rag/service/model/AuthenticationConstants.java index c6bafcd..2460543 100644 --- a/rag-service/src/main/java/com/balex/rag/service/model/AuthenticationConstants.java +++ b/rag-service/src/main/java/com/balex/rag/service/model/AuthenticationConstants.java @@ -11,6 +11,7 @@ public final class AuthenticationConstants { public static final String USER_EMAIL = "email"; public static final String USER_REGISTRATION_STATUS = "userRegistrationStatus"; public static final String LAST_UPDATE = "lastUpdate"; - public static final String ACCESS_KEY_HEADER_NAME = "key"; + public static final String SESSION_ID = "sessionId"; + public static final String ACCESS_KEY_HEADER_NAME = "key"; }