Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.time.Instant;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ExecutionException;

@Component
@RequiredArgsConstructor
Expand All @@ -35,12 +36,20 @@ public void onAuthenticationSuccess(@NotNull HttpServletRequest request, HttpSer
(OAuth2UserInfo) authentication.getPrincipal(), "OAuth2 인증 정보가 존재하지 않습니다.");

// 1. gRPC로 user 서비스 호출
UserResult userResult = userCredentialPort.loadCredential(
userInfo.oauth2Id(),
userInfo.providerType().getProvider(),
userInfo.email(),
userInfo.profileImage()
);
UserResult userResult;
try {
userResult = userCredentialPort.loadCredential(
userInfo.oauth2Id(),
userInfo.providerType().getProvider(),
userInfo.email(),
userInfo.profileImage()
).get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e.getCause());
}

// 2. 일회용 Code 생성 및 Redis 저장
String codeValue = UUID.randomUUID().toString();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package kr.magicbox.auth.adapter.out.communication.grpc;

import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.Futures;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.timelimiter.annotation.TimeLimiter;
import kr.magicbox.auth.adapter.out.communication.grpc.exception.UnsupportedUserRoleException;
import kr.magicbox.auth.adapter.out.communication.grpc.exception.UserServiceUnavailableException;
import kr.magicbox.auth.application.dto.result.UserResult;
Expand All @@ -17,7 +20,7 @@
import org.springframework.grpc.client.GrpcChannelFactory;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.CompletableFuture;

@Slf4j
@Component
Expand All @@ -27,20 +30,22 @@ public class UserGrpcAdapter implements UserCredentialPort {

@Override
@CircuitBreaker(name = "userService", fallbackMethod = "loadCredentialFallback")
public UserResult loadCredential(String oauth2Id, GrpcOAuth2Provider provider, String email, String profileImage) {
@TimeLimiter(name = "userService", fallbackMethod = "loadCredentialFallback")
public CompletableFuture<UserResult> loadCredential(String oauth2Id, GrpcOAuth2Provider provider, String email, String profileImage) {
LoadUserCredentialRequest request = LoadUserCredentialRequest.newBuilder()
.setOauth2Id(oauth2Id)
.setProvider(provider)
.setEmail(email)
.setProfileImage(profileImage != null ? profileImage : "")
.build();

UserServiceGrpc.UserServiceBlockingStub stub = UserServiceGrpc
.newBlockingStub(grpcChannelFactory.createChannel(ServiceHost.USER.getHostName()))
.withDeadlineAfter(2, TimeUnit.SECONDS);
LoadUserCredentialResponse response = stub.loadUserCredential(request);
UserServiceGrpc.UserServiceFutureStub stub = UserServiceGrpc.newFutureStub(
grpcChannelFactory.createChannel(ServiceHost.USER.getHostName()));
ListenableFuture<LoadUserCredentialResponse> future = stub.loadUserCredential(request);
LoadUserCredentialResponse response = Futures.getUnchecked(future);

return new UserResult(UserId.of(response.getUserId()), toUserRole(response.getUserRole()));
return CompletableFuture.completedFuture(
new UserResult(UserId.of(response.getUserId()), toUserRole(response.getUserRole())));
}

private UserRole toUserRole(GrpcUserRole grpcUserRole) {
Expand All @@ -53,7 +58,7 @@ private UserRole toUserRole(GrpcUserRole grpcUserRole) {
}

@SuppressWarnings("unused")
private UserResult loadCredentialFallback(String oauth2Id, GrpcOAuth2Provider provider, String email, String profileImage, Throwable throwable) {
private CompletableFuture<UserResult> loadCredentialFallback(String oauth2Id, GrpcOAuth2Provider provider, String email, String profileImage, Throwable throwable) {
log.warn("User 서비스 연결 실패: {}", throwable.getMessage());
throw new UserServiceUnavailableException(throwable);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package kr.magicbox.auth.adapter.out.communication.grpc;

import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.timelimiter.annotation.TimeLimiter;
import kr.magicbox.auth.adapter.out.communication.grpc.exception.UserServiceUnavailableException;
import kr.magicbox.auth.application.port.out.UserStatusPort;
import kr.magicbox.auth.grpc.user.CheckUserActiveRequest;
Expand All @@ -11,7 +14,7 @@
import org.springframework.grpc.client.GrpcChannelFactory;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.CompletableFuture;

@Slf4j
@Component
Expand All @@ -22,21 +25,22 @@ public class UserStatusGrpcAdapter implements UserStatusPort {

@Override
@CircuitBreaker(name = "userService", fallbackMethod = "checkUserActiveFallback")
public boolean isActive(Long userId) {
@TimeLimiter(name = "userService", fallbackMethod = "checkUserActiveFallback")
public CompletableFuture<Boolean> isActive(Long userId) {
CheckUserActiveRequest request = CheckUserActiveRequest.newBuilder()
.setUserId(userId)
.build();

UserServiceGrpc.UserServiceBlockingStub stub = UserServiceGrpc
.newBlockingStub(grpcChannelFactory.createChannel(ServiceHost.USER.getHostName()))
.withDeadlineAfter(2, TimeUnit.SECONDS);
CheckUserActiveResponse response = stub.checkUserActive(request);
UserServiceGrpc.UserServiceFutureStub stub = UserServiceGrpc.newFutureStub(
grpcChannelFactory.createChannel(ServiceHost.USER.getHostName()));
ListenableFuture<CheckUserActiveResponse> future = stub.checkUserActive(request);
CheckUserActiveResponse response = Futures.getUnchecked(future);

return response.getActive();
return CompletableFuture.completedFuture(response.getActive());
}

@SuppressWarnings("unused")
private boolean checkUserActiveFallback(Long userId, Throwable throwable) {
private CompletableFuture<Boolean> checkUserActiveFallback(Long userId, Throwable throwable) {
log.warn("User 서비스 연결 실패: {}", throwable.getMessage());
throw new UserServiceUnavailableException(throwable);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.interfaces.RSAPublicKey;
import java.util.Date;

@Component
Expand Down Expand Up @@ -90,4 +91,8 @@ public UserRole extractRole(String token) {
String role = claims.get(JwtConstants.CLAIM_ROLE, String.class);
return UserRole.of(role);
}

public RSAPublicKey getPublicKey() {
throw new UnsupportedOperationException("이 서비스는 RSA 공개키를 사용하지 않습니다.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import kr.magicbox.auth.application.dto.result.UserResult;
import kr.magicbox.auth.grpc.user.GrpcOAuth2Provider;

import java.util.concurrent.CompletableFuture;

public interface UserCredentialPort {
UserResult loadCredential(String oauth2Id, GrpcOAuth2Provider provider, String email, String profileImage);
CompletableFuture<UserResult> loadCredential(String oauth2Id, GrpcOAuth2Provider provider, String email, String profileImage);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package kr.magicbox.auth.application.port.out;

import java.util.concurrent.CompletableFuture;

public interface UserStatusPort {
boolean isActive(Long userId);
CompletableFuture<Boolean> isActive(Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.util.concurrent.ExecutionException;

@Service
@RequiredArgsConstructor
Expand Down Expand Up @@ -60,7 +61,15 @@ private Code validateAndGetCode(String codeValue) {

private void saveLoginEvent(UserId userId) {
Instant now = Instant.now();
boolean isDuplicate = userStatusPort.isActive(userId.value());
boolean isDuplicate;
try {
isDuplicate = userStatusPort.isActive(userId.value()).get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e.getCause());
}

AuthDomainEvent event = isDuplicate
? DuplicateLoginEvent.builder().userId(userId).occurredAt(now).build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.util.concurrent.ExecutionException;

@Service
@RequiredArgsConstructor
Expand All @@ -24,7 +25,16 @@ public class LogoutService implements LogoutUseCase {
public void logout(LogoutCommand command) {
UserId userId = command.userId();

if (!userStatusPort.isActive(userId.value())) {
boolean active;
try {
active = userStatusPort.isActive(userId.value()).get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e.getCause());
}
if (!active) {
throw new UserInactiveException();
}
refreshTokenRepositoryPort.deleteRefreshToken(userId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package kr.magicbox.generalgoods.adapter.out.communication.grpc;

import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.timelimiter.annotation.TimeLimiter;
import io.grpc.ManagedChannel;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
Expand All @@ -16,7 +21,7 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.CompletableFuture;

@Component
@RequiredArgsConstructor
Expand All @@ -26,20 +31,33 @@ public class CreatorGrpcAdapter implements CreatorIdQueryPort {

@Override
@CircuitBreaker(name = "creatorService", fallbackMethod = "getCreatorIdFallback")
public CreatorId getCreatorId(UserId userId) {
@TimeLimiter(name = "creatorService", fallbackMethod = "getCreatorIdFallback")
public CompletableFuture<CreatorId> getCreatorId(UserId userId) {
GetCreatorIdByUserIdRequest request = GetCreatorIdByUserIdRequest.newBuilder()
.setUserId(userId.value())
.build();

CreatorServiceGrpc.CreatorServiceBlockingStub stub = CreatorServiceGrpc.newBlockingStub(creatorManagedChannel)
.withDeadlineAfter(2, TimeUnit.SECONDS);
GetCreatorIdByUserIdResponse response = stub.getCreatorIdByUserId(request);
CreatorServiceGrpc.CreatorServiceFutureStub stub = CreatorServiceGrpc.newFutureStub(creatorManagedChannel);
ListenableFuture<GetCreatorIdByUserIdResponse> future = stub.getCreatorIdByUserId(request);

return new CreatorId(response.getCreatorId());
CompletableFuture<CreatorId> result = new CompletableFuture<>();
Futures.addCallback(future, new FutureCallback<GetCreatorIdByUserIdResponse>() {
@Override
public void onSuccess(GetCreatorIdByUserIdResponse response) {
result.complete(new CreatorId(response.getCreatorId()));
}

@Override
public void onFailure(Throwable throwable) {
result.completeExceptionally(throwable);
}
}, MoreExecutors.directExecutor());

return result;
}

@SuppressWarnings("unused")
private CreatorId getCreatorIdFallback(UserId userId, Throwable throwable) {
private CompletableFuture<CreatorId> getCreatorIdFallback(UserId userId, Throwable throwable) {
if (throwable instanceof StatusRuntimeException statusException
&& statusException.getStatus().getCode() == Status.Code.NOT_FOUND) {
throw new CreatorNotFoundException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import kr.magicbox.generalgoods.domain.vo.CreatorId;
import kr.magicbox.generalgoods.domain.vo.UserId;

import java.util.concurrent.CompletableFuture;

public interface CreatorIdQueryPort {

CreatorId getCreatorId(UserId userId);
}
CompletableFuture<CreatorId> getCreatorId(UserId userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ public class DeleteGeneralGoodsService implements DeleteGeneralGoodsUseCase {
public void deleteGeneralGoods(DeleteGeneralGoodsCommand command) {
GeneralGoods generalGoods = generalGoodsRepositoryPort.findById(command.id());

CreatorId creatorId = creatorIdQueryPort.getCreatorId(command.userId());
CreatorId creatorId = creatorIdQueryPort.getCreatorId(command.userId()).join();
if (!generalGoods.getCreatorId().equals(creatorId)) {
throw new GeneralGoodsUnauthorizedException();
}

generalGoods.delete();
generalGoodsRepositoryPort.update(generalGoods);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class RegisterGeneralGoodsService implements RegisterGeneralGoodsUseCase
@Transactional
@Override
public void registerGeneralGoods(RegisterGeneralGoodsCommand command) {
CreatorId creatorId = creatorIdQueryPort.getCreatorId(command.userId());
CreatorId creatorId = creatorIdQueryPort.getCreatorId(command.userId()).join();

List<GeneralGoodsMedia> mediaList = command.mediaList().stream()
.map(this::toGeneralGoodsMedia)
Expand Down Expand Up @@ -71,4 +71,4 @@ private GeneralGoodsMedia toGeneralGoodsMedia(MediaCommand mediaCommand) {
.sortOrder(mediaCommand.sortOrder())
.build();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class UpdateGeneralGoodsService implements UpdateGeneralGoodsUseCase {
public void updateGeneralGoods(UpdateGeneralGoodsCommand command) {
GeneralGoods generalGoods = generalGoodsRepositoryPort.findById(command.id());

CreatorId creatorId = creatorIdQueryPort.getCreatorId(command.userId());
CreatorId creatorId = creatorIdQueryPort.getCreatorId(command.userId()).join();
if (!generalGoods.getCreatorId().equals(creatorId)) {
throw new GeneralGoodsUnauthorizedException();
}
Expand Down Expand Up @@ -78,4 +78,4 @@ private GeneralGoodsMedia toGeneralGoodsMedia(MediaCommand mediaCommand) {
.sortOrder(mediaCommand.sortOrder())
.build();
}
}
}
Loading