Added soft delete to RefreshTokenEntity to prevent deadlock and 500 errors.
This commit is contained in:
parent
0396e8e3b0
commit
3d6577fe02
@ -2,8 +2,10 @@ package app.mealsmadeeasy.api;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
public class MealsMadeEasyApiApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
@ -5,6 +5,7 @@ import app.mealsmadeeasy.api.user.UserEntity;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@ -13,6 +14,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Service
|
||||
public class AuthServiceImpl implements AuthService {
|
||||
@ -76,17 +78,18 @@ public class AuthServiceImpl implements AuthService {
|
||||
final RefreshTokenEntity old = this.refreshTokenRepository.findByToken(refreshToken)
|
||||
.orElseThrow(() -> new LoginException(
|
||||
LoginExceptionReason.INVALID_REFRESH_TOKEN,
|
||||
"No such refresh-token: " + refreshToken
|
||||
"No such refresh token: " + refreshToken
|
||||
));
|
||||
if (old.isRevoked()) {
|
||||
throw new LoginException(LoginExceptionReason.INVALID_REFRESH_TOKEN, "RefreshToken is revoked.");
|
||||
if (old.isRevoked() || old.isDeleted()) {
|
||||
throw new LoginException(LoginExceptionReason.INVALID_REFRESH_TOKEN, "Invalid refresh token.");
|
||||
}
|
||||
if (old.getExpires().isBefore(LocalDateTime.now())) {
|
||||
throw new LoginException(LoginExceptionReason.EXPIRED_REFRESH_TOKEN, "RefreshToken is expired.");
|
||||
throw new LoginException(LoginExceptionReason.EXPIRED_REFRESH_TOKEN, "Refresh token is expired.");
|
||||
}
|
||||
|
||||
final UserEntity principal = old.getOwner();
|
||||
this.refreshTokenRepository.delete(old);
|
||||
old.setDeleted(true);
|
||||
this.refreshTokenRepository.save(old);
|
||||
|
||||
final String username = principal.getUsername();
|
||||
return new LoginDetails(
|
||||
@ -96,4 +99,9 @@ public class AuthServiceImpl implements AuthService {
|
||||
);
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelay = 60, timeUnit = TimeUnit.SECONDS)
|
||||
public void cleanUpDeletedRefreshTokens() {
|
||||
this.refreshTokenRepository.deleteAllWhereSoftDeleted();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,4 +7,5 @@ import java.time.LocalDateTime;
|
||||
public interface RefreshToken extends AuthToken {
|
||||
LocalDateTime getIssued();
|
||||
boolean isRevoked();
|
||||
boolean isDeleted();
|
||||
}
|
||||
|
@ -30,6 +30,9 @@ public class RefreshTokenEntity implements RefreshToken {
|
||||
@ManyToOne
|
||||
private UserEntity owner;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Boolean deleted = false;
|
||||
|
||||
public Long getId() {
|
||||
return this.id;
|
||||
}
|
||||
@ -70,7 +73,7 @@ public class RefreshTokenEntity implements RefreshToken {
|
||||
return this.revoked;
|
||||
}
|
||||
|
||||
public void setRevoked(Boolean revoked) {
|
||||
public void setRevoked(boolean revoked) {
|
||||
this.revoked = revoked;
|
||||
}
|
||||
|
||||
@ -82,6 +85,15 @@ public class RefreshTokenEntity implements RefreshToken {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeleted() {
|
||||
return this.deleted;
|
||||
}
|
||||
|
||||
public void setDeleted(boolean deleted) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLifetime() {
|
||||
return ChronoUnit.SECONDS.between(this.issued, this.expiration);
|
||||
|
@ -1,14 +1,19 @@
|
||||
package app.mealsmadeeasy.api.auth;
|
||||
|
||||
import jakarta.persistence.LockModeType;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Lock;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface RefreshTokenRepository extends JpaRepository<RefreshTokenEntity, Long> {
|
||||
|
||||
@Lock(LockModeType.PESSIMISTIC_READ)
|
||||
Optional<RefreshTokenEntity> findByToken(String token);
|
||||
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query("DELETE FROM RefreshToken t WHERE t.deleted = true")
|
||||
void deleteAllWhereSoftDeleted();
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user