Skip to content

Commit 5db8acd

Browse files
authored
fix : 로그인 시 확인하던 인가(정지계정 유무) 로직 핸드쉐이크 시점으로 이동 (#112)
* feat: 서버 로그 출력 로직 추가 * feat: 인증(jwt)를 담당하는 jwtChannelInterceptor와, 유저가 정지 계정인지를 판단할 userStatusInterceptor 추가 * feat: 인증(jwt)를 담당하는 jwtChannelInterceptor와, 유저가 정지 계정인지를 판단할 userStatusInterceptor 추가 * refactor : 로그인 시 확인하던 인가(정지계정 유무) 로직 삭제
1 parent 34141eb commit 5db8acd

File tree

6 files changed

+109
-36
lines changed

6 files changed

+109
-36
lines changed

gotcha-socket/src/main/java/socket_server/common/auth/JwtChannelInterceptor.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ public Message<?> preSend(Message<?> message, MessageChannel channel) {
4848
} catch (AuthenticationServiceException e) {
4949
throw new MessagingException(toErrorPayload(JwtExceptionCode.ACCESS_TOKEN_NOT_FOUND));
5050
} catch (Throwable e) {
51+
System.err.println("=== [DEBUG][JwtChannelInterceptor] 예외 발생 ===");
52+
e.printStackTrace();
53+
5154
throw new MessagingException(toErrorPayload(GlobalExceptionCode.INTERNAL_SERVER_ERROR));
5255
}
5356
}
@@ -64,5 +67,4 @@ private String toErrorPayload(ExceptionCode code) {
6467
}
6568

6669

67-
6870
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package socket_server.common.auth;
2+
3+
import gotcha_common.exception.exceptionCode.ExceptionCode;
4+
import gotcha_common.exception.exceptionCode.GlobalExceptionCode;
5+
import gotcha_domain.auth.SecurityUserDetails;
6+
import gotcha_domain.user.User;
7+
import lombok.RequiredArgsConstructor;
8+
import org.springframework.messaging.Message;
9+
import org.springframework.messaging.MessageChannel;
10+
import org.springframework.messaging.MessagingException;
11+
import org.springframework.messaging.simp.stomp.StompCommand;
12+
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
13+
import org.springframework.messaging.support.ChannelInterceptor;
14+
import org.springframework.messaging.support.MessageHeaderAccessor;
15+
import org.springframework.security.core.Authentication;
16+
import org.springframework.stereotype.Component;
17+
import socket_server.common.exception.socket.SocketUserStatusExceptionCode;
18+
19+
@Component
20+
@RequiredArgsConstructor
21+
public class UserStatusInterceptor implements ChannelInterceptor {
22+
23+
@Override
24+
public Message<?> preSend(Message<?> message, MessageChannel channel) {
25+
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
26+
27+
try {
28+
if (accessor != null && StompCommand.CONNECT.equals(accessor.getCommand())) {
29+
Authentication auth = (Authentication) accessor.getUser();
30+
SecurityUserDetails details = (SecurityUserDetails) auth.getPrincipal();
31+
User user = details.getUser();
32+
33+
SocketUserStatusExceptionCode code =
34+
SocketUserStatusExceptionCode.fromStatus(user.getUserStatus());
35+
36+
if (code != null) {
37+
throw new MessagingException(toErrorPayload(code));
38+
}
39+
40+
}
41+
42+
return message;
43+
} catch (Throwable e) {
44+
if (e instanceof MessagingException) {
45+
throw (MessagingException) e;
46+
}
47+
System.err.println("=== [DEBUG][UserStatusInterceptor] 예외 발생 ===");
48+
e.printStackTrace();
49+
50+
throw new MessagingException(toErrorPayload(GlobalExceptionCode.INTERNAL_SERVER_ERROR));
51+
}
52+
}
53+
54+
private String toErrorPayload(ExceptionCode code) {
55+
return String.format("{\"errorCode\":\"%s\", \"status\":%d, \"message\":\"%s\"}", code.getCode(),
56+
code.getStatus().value(), code.getMessage());
57+
}
58+
}

gotcha-socket/src/main/java/socket_server/common/config/WebSocketConfig.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,22 @@
88
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
99
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
1010
import socket_server.common.auth.JwtChannelInterceptor;
11+
import socket_server.common.auth.UserStatusInterceptor;
1112

1213
@Configuration
1314
@EnableWebSocketMessageBroker
1415
@RequiredArgsConstructor
1516
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
1617
private static final String ENDPOINT = "/ws-connect";
1718
private final JwtChannelInterceptor jwtChannelInterceptor;
19+
private final UserStatusInterceptor userStatusInterceptor;
1820

1921
@Override
2022
public void configureClientInboundChannel(ChannelRegistration registration) {
21-
registration.interceptors(jwtChannelInterceptor);
23+
registration.interceptors(
24+
jwtChannelInterceptor, // 1. 인증(jwt)
25+
userStatusInterceptor // 2. 인가(정지 계정)
26+
);
2227
}
2328

2429
//레디스 메시지 브로커 사용
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package socket_server.common.exception.socket;
2+
3+
import gotcha_common.exception.exceptionCode.ExceptionCode;
4+
import gotcha_domain.user.UserStatus;
5+
import lombok.AllArgsConstructor;
6+
import org.springframework.http.HttpStatus;
7+
8+
@AllArgsConstructor
9+
public enum SocketUserStatusExceptionCode implements ExceptionCode {
10+
USER_SUSPENDED(HttpStatus.FORBIDDEN, "SOCKET-403-001", "계정이 일시 정지되었습니다."),
11+
USER_BANNED(HttpStatus.FORBIDDEN, "SOCKET-403-002", "계정이 영구 정지되었습니다.");
12+
13+
private final HttpStatus status;
14+
private final String code;
15+
private final String message;
16+
17+
@Override
18+
public HttpStatus getStatus() {
19+
return status;
20+
}
21+
22+
@Override
23+
public String getCode() {
24+
return code;
25+
}
26+
27+
@Override
28+
public String getMessage() {
29+
return message;
30+
}
31+
32+
public static SocketUserStatusExceptionCode fromStatus(UserStatus status) {
33+
return switch (status) {
34+
case SUSPENDED -> USER_SUSPENDED;
35+
case BANNED -> USER_BANNED;
36+
default -> null; // 정상 계정
37+
};
38+
}
39+
}

gotcha/src/main/java/Gotcha/domain/auth/service/SignInUseCase.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,13 @@ public TokenDto execute(SignInReq signInReq) {
2727
// 2. 정지기간 만료되었는지 확인
2828
sanctionService.validateSuspendedEndDate(user);
2929

30-
// 3. 제재/차단 상태 확인
31-
sanctionService.validateLoginAccess(user);
32-
33-
// 4. 경고 확인
30+
// 3. 경고 확인
3431
Optional<SanctionRes> warningOpt = sanctionService.findAndMarkUnreadWarning(user);
3532

36-
// 5. 토큰 생성
33+
// 4. 토큰 생성
3734
TokenDto tokenDto = jwtHelper.createToken(user, signInReq.autoSignIn());
3835

39-
// 6. 경고 메시지가 있으면 토큰에 추가하여 반환
36+
// 5. 경고 메시지가 있으면 토큰에 추가하여 반환
4037
return warningOpt
4138
.map(warning -> TokenDto.of(tokenDto.accessToken(), tokenDto.refreshToken(), tokenDto.accessTokenExpiredAt(), tokenDto.autoSignIn(), warning))
4239
.orElse(tokenDto);

gotcha/src/main/java/Gotcha/domain/sanction/service/SanctionService.java

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package Gotcha.domain.sanction.service;
22

3-
import Gotcha.domain.auth.exception.AuthExceptionCode;
4-
import Gotcha.domain.auth.exception.UserAccountStatusException;
53
import Gotcha.domain.report.service.UserReportService;
64
import Gotcha.domain.sanction.dto.SanctionReq;
75
import Gotcha.domain.sanction.dto.SanctionRes;
@@ -14,13 +12,10 @@
1412
import gotcha_domain.user.User;
1513
import gotcha_domain.user.UserStatus;
1614
import gotcha_user.service.UserService;
17-
import jakarta.persistence.TableGenerator;
1815
import lombok.RequiredArgsConstructor;
1916
import org.springframework.stereotype.Service;
2017
import org.springframework.transaction.annotation.Transactional;
2118

22-
import java.util.HashMap;
23-
import java.util.Map;
2419
import java.util.Optional;
2520

2621
@Service
@@ -67,25 +62,6 @@ public SanctionRes sanctionUser(SanctionReq sanctionReq, String adminId) {
6762
return SanctionRes.fromEntity(userSanction);
6863
}
6964

70-
@Transactional
71-
public void validateLoginAccess(User user) {
72-
if (user.getUserStatus() == UserStatus.SUSPENDED || user.getUserStatus() == UserStatus.BANNED) {
73-
AuthExceptionCode code = user.getUserStatus() == UserStatus.SUSPENDED ?
74-
AuthExceptionCode.ACCOUNT_SUSPENDED : AuthExceptionCode.ACCOUNT_BANNED;
75-
76-
UserSanction sanction = findLatestUnread(user)
77-
.orElseThrow(()->new CustomException(AuthExceptionCode.SANCTION_NOT_FOUND));
78-
sanction.markAsRead();
79-
80-
Map<String, Object> details = new HashMap<>();
81-
details.put("reason", sanction.getReason());
82-
if (sanction.getExpireDuration() != null) {
83-
details.put("expireDuration", sanction.getExpireDuration());
84-
}
85-
throw new UserAccountStatusException(code, details);
86-
}
87-
}
88-
8965
@Transactional
9066
public void validateSuspendedEndDate(User user) {
9167
if(user.getUserStatus()==UserStatus.SUSPENDED) {
@@ -102,10 +78,6 @@ public Optional<SanctionRes> findAndMarkUnreadWarning(User user) {
10278
});
10379
}
10480

105-
public Optional<UserSanction> findLatestUnread(User user) {
106-
return sanctionRepository.findTopByUserAndIsReadIsFalseOrderByCreatedAtDesc(user);
107-
}
108-
10981
public Optional<UserSanction> findLatestUnread(User user, SanctionType type) {
11082
return sanctionRepository.findTopByUserAndSanctionTypeAndIsReadIsFalseOrderByCreatedAtDesc(user, type);
11183
}

0 commit comments

Comments
 (0)