From 31c7d4576434e5b92c99d99a26dbda0975c0ccbf Mon Sep 17 00:00:00 2001 From: thdwoqor Date: Wed, 4 Dec 2024 16:47:00 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20=EC=9B=B9=EC=86=8C=EC=BC=93=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=20=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mafiatogether/chat/ui/ChatController.java | 28 +++---- .../ui/WebsocketPlayerArgumentResolver.java | 35 ++++++++ .../common/config/WebSocketConfig.java | 20 ++++- .../common/interceptor/ChatInterceptor.java | 37 +++++++++ .../interceptor/PathMatcherInterceptor.java | 70 ++++++++++++++++ .../interceptor/StompChannelInterceptor.java | 80 ------------------- .../common/interceptor/StompMapping.java | 9 +++ src/main/resources/static/app.js | 25 ++++-- 8 files changed, 199 insertions(+), 105 deletions(-) create mode 100644 src/main/java/mafia/mafiatogether/chat/ui/WebsocketPlayerArgumentResolver.java create mode 100644 src/main/java/mafia/mafiatogether/common/interceptor/ChatInterceptor.java create mode 100644 src/main/java/mafia/mafiatogether/common/interceptor/PathMatcherInterceptor.java delete mode 100644 src/main/java/mafia/mafiatogether/common/interceptor/StompChannelInterceptor.java create mode 100644 src/main/java/mafia/mafiatogether/common/interceptor/StompMapping.java diff --git a/src/main/java/mafia/mafiatogether/chat/ui/ChatController.java b/src/main/java/mafia/mafiatogether/chat/ui/ChatController.java index e91cbb2..5da57fd 100644 --- a/src/main/java/mafia/mafiatogether/chat/ui/ChatController.java +++ b/src/main/java/mafia/mafiatogether/chat/ui/ChatController.java @@ -1,5 +1,6 @@ package mafia.mafiatogether.chat.ui; +import java.util.List; import lombok.RequiredArgsConstructor; import mafia.mafiatogether.chat.annotation.SendToChatWithRedis; import mafia.mafiatogether.chat.application.ChatService; @@ -15,8 +16,6 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; -import java.util.List; - @Controller @RequiredArgsConstructor public class ChatController { @@ -28,33 +27,32 @@ public ResponseEntity> findAllChat(@PlayerInfo PlayerInfoDto return ResponseEntity.ok(chatService.findAllChat(playerInfoDto.code(), playerInfoDto.name())); } - @MessageMapping("/chat/enter/{code}/{name}") + @MessageMapping("/chat/{code}/enter") @SendToChatWithRedis("/sub/chat/{code}") public Message enterChat( @DestinationVariable("code") String code, - @DestinationVariable("name") String name + @PlayerInfo PlayerInfoDto playerInfoDto ) { - return chatService.enter(name, code); + return chatService.enter(playerInfoDto.name(), code); } - @MessageMapping("/chat/leave/{code}/{name}") + @MessageMapping("/chat/{code}") @SendToChatWithRedis("/sub/chat/{code}") - public Message leaveChat( + public Message createChat( @DestinationVariable("code") String code, - @DestinationVariable("name") String name + @PlayerInfo PlayerInfoDto playerInfoDto, + @Payload ChatRequest request ) { - return chatService.leave(name, code); + return chatService.chat(playerInfoDto.name(), code, request.content()); } - @MessageMapping("/chat/{code}/{name}") + @MessageMapping("/chat/{code}/leave") @SendToChatWithRedis("/sub/chat/{code}") - public Message createChat( + public Message leaveChat( @DestinationVariable("code") String code, - @DestinationVariable("name") String name, - @Payload ChatRequest request + @PlayerInfo PlayerInfoDto playerInfoDto ) { - return chatService.chat(name, code, request.content()); + return chatService.leave(playerInfoDto.name(), code); } - } diff --git a/src/main/java/mafia/mafiatogether/chat/ui/WebsocketPlayerArgumentResolver.java b/src/main/java/mafia/mafiatogether/chat/ui/WebsocketPlayerArgumentResolver.java new file mode 100644 index 0000000..fe86a1f --- /dev/null +++ b/src/main/java/mafia/mafiatogether/chat/ui/WebsocketPlayerArgumentResolver.java @@ -0,0 +1,35 @@ +package mafia.mafiatogether.chat.ui; + +import mafia.mafiatogether.common.annotation.PlayerInfo; +import mafia.mafiatogether.common.exception.AuthException; +import mafia.mafiatogether.common.exception.ExceptionCode; +import mafia.mafiatogether.common.resolver.PlayerInfoDto; +import mafia.mafiatogether.common.util.AuthExtractor; +import org.springframework.core.MethodParameter; +import org.springframework.messaging.Message; +import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; +import org.springframework.messaging.simp.SimpMessageHeaderAccessor; + +public class WebsocketPlayerArgumentResolver implements HandlerMethodArgumentResolver { + @Override + public boolean supportsParameter(final MethodParameter parameter) { + return parameter.hasParameterAnnotation(PlayerInfo.class); + } + + @Override + public Object resolveArgument(final MethodParameter parameter, final Message message) throws Exception { + SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.wrap(message); + String authorization = headerAccessor.getFirstNativeHeader("Authorization"); + if (authorization == null) { + throw new AuthException(ExceptionCode.MISSING_AUTHENTICATION_HEADER); + } + if (!authorization.startsWith("Basic")) { + throw new AuthException(ExceptionCode.MISSING_AUTHENTICATION_HEADER); + } + + String[] information = AuthExtractor.extractByAuthorization(authorization); + + return new PlayerInfoDto(information[0], information[1]); + } + +} diff --git a/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java b/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java index b5e20e9..6a05a5e 100644 --- a/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java +++ b/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java @@ -1,11 +1,16 @@ package mafia.mafiatogether.common.config; +import java.util.List; import lombok.RequiredArgsConstructor; -import mafia.mafiatogether.common.interceptor.StompChannelInterceptor; +import mafia.mafiatogether.chat.ui.WebsocketPlayerArgumentResolver; +import mafia.mafiatogether.common.interceptor.ChatInterceptor; +import mafia.mafiatogether.common.interceptor.PathMatcherInterceptor; import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.messaging.simp.stomp.StompCommand; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; @@ -15,7 +20,7 @@ @RequiredArgsConstructor public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { - private final StompChannelInterceptor stompChannelInterceptor; + private final ChatInterceptor chatInterceptor; @Override public void registerStompEndpoints(StompEndpointRegistry registry) { @@ -34,9 +39,18 @@ public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/sub"); } + @Override + public void addArgumentResolvers(final List argumentResolvers) { + argumentResolvers.add(new WebsocketPlayerArgumentResolver()); + } + @Override public void configureClientInboundChannel(ChannelRegistration registry) { - registry.interceptors(stompChannelInterceptor); + registry.interceptors( + new PathMatcherInterceptor(new ChatInterceptor()) + .includePathPattern("/chat/**", StompCommand.SUBSCRIBE) + .includePathPattern("/chat/**", StompCommand.SEND) + ); } } diff --git a/src/main/java/mafia/mafiatogether/common/interceptor/ChatInterceptor.java b/src/main/java/mafia/mafiatogether/common/interceptor/ChatInterceptor.java new file mode 100644 index 0000000..3ee00dc --- /dev/null +++ b/src/main/java/mafia/mafiatogether/common/interceptor/ChatInterceptor.java @@ -0,0 +1,37 @@ +package mafia.mafiatogether.common.interceptor; + +import mafia.mafiatogether.common.exception.AuthException; +import mafia.mafiatogether.common.exception.ExceptionCode; +import mafia.mafiatogether.common.util.AuthExtractor; +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.simp.stomp.StompHeaderAccessor; +import org.springframework.messaging.support.ChannelInterceptor; +import org.springframework.messaging.support.MessageHeaderAccessor; +import org.springframework.stereotype.Component; + +@Component +@Configuration +public class ChatInterceptor implements ChannelInterceptor { + + @Override + public Message preSend(Message message, MessageChannel channel) { + StompHeaderAccessor headerAccessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); + assert headerAccessor != null; + + String authorization = String.valueOf(headerAccessor.getFirstNativeHeader("Authorization")); + + if (authorization == null) { + throw new AuthException(ExceptionCode.MISSING_AUTHENTICATION_HEADER); + } + if (!authorization.startsWith("Basic")) { + throw new AuthException(ExceptionCode.MISSING_AUTHENTICATION_HEADER); + } + + //검증 + String[] information = AuthExtractor.extractByAuthorization(authorization); + return message; + } + +} diff --git a/src/main/java/mafia/mafiatogether/common/interceptor/PathMatcherInterceptor.java b/src/main/java/mafia/mafiatogether/common/interceptor/PathMatcherInterceptor.java new file mode 100644 index 0000000..83fed57 --- /dev/null +++ b/src/main/java/mafia/mafiatogether/common/interceptor/PathMatcherInterceptor.java @@ -0,0 +1,70 @@ +package mafia.mafiatogether.common.interceptor; + +import java.util.ArrayList; +import java.util.List; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.simp.stomp.StompCommand; +import org.springframework.messaging.simp.stomp.StompHeaderAccessor; +import org.springframework.messaging.support.ChannelInterceptor; +import org.springframework.messaging.support.MessageHeaderAccessor; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; + +public class PathMatcherInterceptor implements ChannelInterceptor { + + private final ChannelInterceptor channelInterceptor; + private final PathMatcher pathMatcher; + private final List includePathPattern; + private final List excludePathPattern; + + public PathMatcherInterceptor( + final ChannelInterceptor channelInterceptor + ) { + this.channelInterceptor = channelInterceptor; + this.pathMatcher = new AntPathMatcher(); + this.includePathPattern = new ArrayList<>(); + this.excludePathPattern = new ArrayList<>(); + } + + + @Override + public Message preSend(final Message message, final MessageChannel channel) { + StompHeaderAccessor headerAccessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); + + if ( + headerAccessor.getDestination() != null && + notIncludedPath(headerAccessor.getDestination(), headerAccessor.getCommand()) + ) { + return channelInterceptor.preSend(message, channel); + } + + return ChannelInterceptor.super.preSend(message, channel); + } + + private boolean notIncludedPath(String destination, StompCommand command) { + boolean excludePattern = excludePathPattern.stream() + .anyMatch(stompMapping -> anyMatchPathPattern(destination, command, stompMapping)); + + boolean includePattern = includePathPattern.stream() + .anyMatch(stompMapping -> anyMatchPathPattern(destination, command, stompMapping)); + + return excludePattern || !includePattern; + } + + private boolean anyMatchPathPattern(String destination, StompCommand command, StompMapping stompMapping) { + return pathMatcher.match(destination, stompMapping.destination()) && + stompMapping.command() == command; + } + + public PathMatcherInterceptor includePathPattern(String targetPath, StompCommand pathMethod) { + this.includePathPattern.add(new StompMapping(targetPath, pathMethod)); + return this; + } + + public PathMatcherInterceptor excludePathPattern(String targetPath, StompCommand pathMethod) { + this.excludePathPattern.add(new StompMapping(targetPath, pathMethod)); + return this; + } + +} diff --git a/src/main/java/mafia/mafiatogether/common/interceptor/StompChannelInterceptor.java b/src/main/java/mafia/mafiatogether/common/interceptor/StompChannelInterceptor.java deleted file mode 100644 index 435dc4b..0000000 --- a/src/main/java/mafia/mafiatogether/common/interceptor/StompChannelInterceptor.java +++ /dev/null @@ -1,80 +0,0 @@ -package mafia.mafiatogether.common.interceptor; - -import java.util.Map; -import java.util.Objects; -import java.util.function.Consumer; -import lombok.RequiredArgsConstructor; -import mafia.mafiatogether.common.util.AuthExtractor; -import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageChannel; -import org.springframework.messaging.simp.stomp.StompCommand; -import org.springframework.messaging.simp.stomp.StompHeaderAccessor; -import org.springframework.messaging.support.ChannelInterceptor; -import org.springframework.messaging.support.MessageHeaderAccessor; -import org.springframework.stereotype.Component; - -@Component -@Configuration -@RequiredArgsConstructor -public class StompChannelInterceptor implements ChannelInterceptor { - - private static final String SUBSCRIBE_FORMAT = "%s/%s"; - private static final String PUBLISHING_FORMAT = "%s/%s/%s"; - - private final Map> actionByCommand = Map.of( - StompCommand.SUBSCRIBE, this::consumeWhenSubscribe, - StompCommand.SEND, this::consumeWhenPublish - ); - - @Override - public Message preSend(Message message, MessageChannel channel) { - StompHeaderAccessor headerAccessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); - if (Objects.isNull(headerAccessor)) { - return message; - } - StompCommand command = headerAccessor.getCommand(); - if (!actionByCommand.containsKey(command)) { - return message; - } - actionByCommand.get(command).accept(headerAccessor); - return message; - } - - private void consumeWhenSubscribe(StompHeaderAccessor headerAccessor) { - String[] information = getInformation(headerAccessor); - String prefixUrl = headerAccessor.getDestination() - .substring(0, headerAccessor.getDestination().lastIndexOf('/')); - headerAccessor.setDestination(SUBSCRIBE_FORMAT.formatted(prefixUrl, information[0])); - } - - private String[] getInformation(StompHeaderAccessor headerAccessor) { - String destination = headerAccessor.getDestination(); - int lastIndexOfSlash = getLastIndexOfSlash(destination); - String code = destination.substring(lastIndexOfSlash + 1); - - return AuthExtractor.extractByCode(code); - } - - private int getLastIndexOfSlash(String destination) { - if (destination == null) { - throw new IllegalArgumentException("Destination이 존재하지 않습니다."); - } - - int lastIndexOfSlash = destination.lastIndexOf('/'); - - if (lastIndexOfSlash == -1) { - throw new IllegalArgumentException("올바르지 않은 Destintation 입니다."); - } - - return lastIndexOfSlash; - } - - private void consumeWhenPublish(StompHeaderAccessor headerAccessor) { - String[] information = getInformation(headerAccessor); - String prefixUrl = headerAccessor.getDestination() - .substring(0, headerAccessor.getDestination().lastIndexOf('/')); - headerAccessor.setDestination(PUBLISHING_FORMAT.formatted(prefixUrl, information[0], information[1])); - } - -} diff --git a/src/main/java/mafia/mafiatogether/common/interceptor/StompMapping.java b/src/main/java/mafia/mafiatogether/common/interceptor/StompMapping.java new file mode 100644 index 0000000..686145c --- /dev/null +++ b/src/main/java/mafia/mafiatogether/common/interceptor/StompMapping.java @@ -0,0 +1,9 @@ +package mafia.mafiatogether.common.interceptor; + +import org.springframework.messaging.simp.stomp.StompCommand; + +public record StompMapping( + String destination, + StompCommand command +) { +} diff --git a/src/main/resources/static/app.js b/src/main/resources/static/app.js index 818333e..263c82c 100644 --- a/src/main/resources/static/app.js +++ b/src/main/resources/static/app.js @@ -1,12 +1,14 @@ const stompClient = new StompJs.Client({ - brokerURL: 'ws://localhost:8080/stomp' + brokerURL: 'ws://localhost:8080/stomp', }); stompClient.onConnect = (frame) => { setConnected(true); console.log('Connected: ' + frame); - stompClient.subscribe('/sub/chat/aGVsbG86cG93ZXJhc3M=', (greeting) => { + stompClient.subscribe('/sub/chat/hello', (greeting) => { showGreeting(JSON.parse(greeting.body)); + },{ + Authorization: 'Basic aGVsbG86cG93ZXJhc3M=', }); }; @@ -42,20 +44,29 @@ function disconnect() { function sendName() { stompClient.publish({ - destination: "/pub/chat/aGVsbG86cG93ZXJhc3M=", - body: JSON.stringify({'content': $("#name").val()}) + destination: "/pub/chat/hello", + body: JSON.stringify({'content': $("#name").val()}), + headers: { + Authorization: 'Basic aGVsbG86cG93ZXJhc3M=', + }, }); } function enterRoom() { stompClient.publish({ - destination: "/pub/chat/enter/aGVsbG86cG93ZXJhc3M=", + destination: "/pub/chat/hello/enter", + headers: { + Authorization: 'Basic aGVsbG86cG93ZXJhc3M=', + }, }); } function leaveRoom() { stompClient.publish({ - destination: "/pub/chat/leave/aGVsbG86cG93ZXJhc3M=", + destination: "/pub/chat/hello/leave", + headers: { + Authorization: 'Basic aGVsbG86cG93ZXJhc3M=', + }, }); } @@ -71,4 +82,4 @@ $(function () { $("#send").click(() => sendName()); $("#enter").click(() => enterRoom()); $("#leave").click(() => leaveRoom()); -}); \ No newline at end of file +}); From a7cfaa1aa893b18764689f642acb7fdbb9869614 Mon Sep 17 00:00:00 2001 From: thdwoqor Date: Wed, 25 Dec 2024 20:03:02 +0900 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20=EC=B1=84=ED=8C=85=20=EB=A1=A4?= =?UTF-8?q?=EB=B0=B1=20Path=20=EB=A1=A4=EB=B0=B1=20=EB=B0=8F=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=EC=85=89=ED=84=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mafiatogether/chat/ui/ChatController.java | 24 +++---- .../ui/WebsocketPlayerArgumentResolver.java | 35 ----------- .../common/config/WebSocketConfig.java | 13 +--- .../common/interceptor/ChatInterceptor.java | 63 +++++++++++++++---- .../resolver/PlayerArgumentResolver.java | 3 +- src/main/resources/static/app.js | 23 ++----- 6 files changed, 74 insertions(+), 87 deletions(-) delete mode 100644 src/main/java/mafia/mafiatogether/chat/ui/WebsocketPlayerArgumentResolver.java diff --git a/src/main/java/mafia/mafiatogether/chat/ui/ChatController.java b/src/main/java/mafia/mafiatogether/chat/ui/ChatController.java index 5da57fd..6bd5d51 100644 --- a/src/main/java/mafia/mafiatogether/chat/ui/ChatController.java +++ b/src/main/java/mafia/mafiatogether/chat/ui/ChatController.java @@ -27,32 +27,32 @@ public ResponseEntity> findAllChat(@PlayerInfo PlayerInfoDto return ResponseEntity.ok(chatService.findAllChat(playerInfoDto.code(), playerInfoDto.name())); } - @MessageMapping("/chat/{code}/enter") + @MessageMapping("/chat/enter/{code}/{name}") @SendToChatWithRedis("/sub/chat/{code}") public Message enterChat( @DestinationVariable("code") String code, - @PlayerInfo PlayerInfoDto playerInfoDto + @DestinationVariable("name") String name ) { - return chatService.enter(playerInfoDto.name(), code); + return chatService.enter(name, code); } - @MessageMapping("/chat/{code}") + @MessageMapping("/chat/leave/{code}/{name}") @SendToChatWithRedis("/sub/chat/{code}") - public Message createChat( + public Message leaveChat( @DestinationVariable("code") String code, - @PlayerInfo PlayerInfoDto playerInfoDto, - @Payload ChatRequest request + @DestinationVariable("name") String name ) { - return chatService.chat(playerInfoDto.name(), code, request.content()); + return chatService.leave(name, code); } - @MessageMapping("/chat/{code}/leave") + @MessageMapping("/chat/{code}/{name}") @SendToChatWithRedis("/sub/chat/{code}") - public Message leaveChat( + public Message createChat( @DestinationVariable("code") String code, - @PlayerInfo PlayerInfoDto playerInfoDto + @DestinationVariable("name") String name, + @Payload ChatRequest request ) { - return chatService.leave(playerInfoDto.name(), code); + return chatService.chat(name, code, request.content()); } } diff --git a/src/main/java/mafia/mafiatogether/chat/ui/WebsocketPlayerArgumentResolver.java b/src/main/java/mafia/mafiatogether/chat/ui/WebsocketPlayerArgumentResolver.java deleted file mode 100644 index fe86a1f..0000000 --- a/src/main/java/mafia/mafiatogether/chat/ui/WebsocketPlayerArgumentResolver.java +++ /dev/null @@ -1,35 +0,0 @@ -package mafia.mafiatogether.chat.ui; - -import mafia.mafiatogether.common.annotation.PlayerInfo; -import mafia.mafiatogether.common.exception.AuthException; -import mafia.mafiatogether.common.exception.ExceptionCode; -import mafia.mafiatogether.common.resolver.PlayerInfoDto; -import mafia.mafiatogether.common.util.AuthExtractor; -import org.springframework.core.MethodParameter; -import org.springframework.messaging.Message; -import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; -import org.springframework.messaging.simp.SimpMessageHeaderAccessor; - -public class WebsocketPlayerArgumentResolver implements HandlerMethodArgumentResolver { - @Override - public boolean supportsParameter(final MethodParameter parameter) { - return parameter.hasParameterAnnotation(PlayerInfo.class); - } - - @Override - public Object resolveArgument(final MethodParameter parameter, final Message message) throws Exception { - SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.wrap(message); - String authorization = headerAccessor.getFirstNativeHeader("Authorization"); - if (authorization == null) { - throw new AuthException(ExceptionCode.MISSING_AUTHENTICATION_HEADER); - } - if (!authorization.startsWith("Basic")) { - throw new AuthException(ExceptionCode.MISSING_AUTHENTICATION_HEADER); - } - - String[] information = AuthExtractor.extractByAuthorization(authorization); - - return new PlayerInfoDto(information[0], information[1]); - } - -} diff --git a/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java b/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java index 6a05a5e..57a7d0e 100644 --- a/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java +++ b/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java @@ -1,13 +1,10 @@ package mafia.mafiatogether.common.config; -import java.util.List; import lombok.RequiredArgsConstructor; -import mafia.mafiatogether.chat.ui.WebsocketPlayerArgumentResolver; import mafia.mafiatogether.common.interceptor.ChatInterceptor; import mafia.mafiatogether.common.interceptor.PathMatcherInterceptor; import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.messaging.simp.stomp.StompCommand; @@ -39,17 +36,13 @@ public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/sub"); } - @Override - public void addArgumentResolvers(final List argumentResolvers) { - argumentResolvers.add(new WebsocketPlayerArgumentResolver()); - } - @Override public void configureClientInboundChannel(ChannelRegistration registry) { registry.interceptors( - new PathMatcherInterceptor(new ChatInterceptor()) + new PathMatcherInterceptor(chatInterceptor) .includePathPattern("/chat/**", StompCommand.SUBSCRIBE) - .includePathPattern("/chat/**", StompCommand.SEND) + .includePathPattern("/chat/**", StompCommand.DISCONNECT) + .includePathPattern("/chat/**", StompCommand.MESSAGE) ); } diff --git a/src/main/java/mafia/mafiatogether/common/interceptor/ChatInterceptor.java b/src/main/java/mafia/mafiatogether/common/interceptor/ChatInterceptor.java index 3ee00dc..427ae6a 100644 --- a/src/main/java/mafia/mafiatogether/common/interceptor/ChatInterceptor.java +++ b/src/main/java/mafia/mafiatogether/common/interceptor/ChatInterceptor.java @@ -1,11 +1,13 @@ package mafia.mafiatogether.common.interceptor; -import mafia.mafiatogether.common.exception.AuthException; -import mafia.mafiatogether.common.exception.ExceptionCode; +import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; import mafia.mafiatogether.common.util.AuthExtractor; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.simp.stomp.StompCommand; import org.springframework.messaging.simp.stomp.StompHeaderAccessor; import org.springframework.messaging.support.ChannelInterceptor; import org.springframework.messaging.support.MessageHeaderAccessor; @@ -15,23 +17,62 @@ @Configuration public class ChatInterceptor implements ChannelInterceptor { + private static final String SUBSCRIBE_FORMAT = "%s/%s"; + private static final String PUBLISHING_FORMAT = "%s/%s/%s"; + + private final Map> actionByCommand = Map.of( + StompCommand.SUBSCRIBE, this::consumeWhenSubscribe, + StompCommand.SEND, this::consumeWhenPublish + ); + @Override public Message preSend(Message message, MessageChannel channel) { StompHeaderAccessor headerAccessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); - assert headerAccessor != null; + if (Objects.isNull(headerAccessor)) { + return message; + } + StompCommand command = headerAccessor.getCommand(); + if (!actionByCommand.containsKey(command)) { + return message; + } + actionByCommand.get(command).accept(headerAccessor); + return message; + } + + private void consumeWhenSubscribe(StompHeaderAccessor headerAccessor) { + String[] information = getInformation(headerAccessor); + String prefixUrl = headerAccessor.getDestination() + .substring(0, headerAccessor.getDestination().lastIndexOf('/')); + headerAccessor.setDestination(SUBSCRIBE_FORMAT.formatted(prefixUrl, information[0])); + } - String authorization = String.valueOf(headerAccessor.getFirstNativeHeader("Authorization")); + private String[] getInformation(StompHeaderAccessor headerAccessor) { + String destination = headerAccessor.getDestination(); + int lastIndexOfSlash = getLastIndexOfSlash(destination); + String code = destination.substring(lastIndexOfSlash + 1); - if (authorization == null) { - throw new AuthException(ExceptionCode.MISSING_AUTHENTICATION_HEADER); + return AuthExtractor.extractByCode(code); + } + + private int getLastIndexOfSlash(String destination) { + if (destination == null) { + throw new IllegalArgumentException("Destination이 존재하지 않습니다."); } - if (!authorization.startsWith("Basic")) { - throw new AuthException(ExceptionCode.MISSING_AUTHENTICATION_HEADER); + + int lastIndexOfSlash = destination.lastIndexOf('/'); + + if (lastIndexOfSlash == -1) { + throw new IllegalArgumentException("올바르지 않은 Destintation 입니다."); } - //검증 - String[] information = AuthExtractor.extractByAuthorization(authorization); - return message; + return lastIndexOfSlash; + } + + private void consumeWhenPublish(StompHeaderAccessor headerAccessor) { + String[] information = getInformation(headerAccessor); + String prefixUrl = headerAccessor.getDestination() + .substring(0, headerAccessor.getDestination().lastIndexOf('/')); + headerAccessor.setDestination(PUBLISHING_FORMAT.formatted(prefixUrl, information[0], information[1])); } } diff --git a/src/main/java/mafia/mafiatogether/common/resolver/PlayerArgumentResolver.java b/src/main/java/mafia/mafiatogether/common/resolver/PlayerArgumentResolver.java index 945d1a8..958b844 100644 --- a/src/main/java/mafia/mafiatogether/common/resolver/PlayerArgumentResolver.java +++ b/src/main/java/mafia/mafiatogether/common/resolver/PlayerArgumentResolver.java @@ -1,11 +1,10 @@ package mafia.mafiatogether.common.resolver; import jakarta.servlet.http.HttpServletRequest; - -import mafia.mafiatogether.common.util.AuthExtractor; import mafia.mafiatogether.common.annotation.PlayerInfo; import mafia.mafiatogether.common.exception.AuthException; import mafia.mafiatogether.common.exception.ExceptionCode; +import mafia.mafiatogether.common.util.AuthExtractor; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; diff --git a/src/main/resources/static/app.js b/src/main/resources/static/app.js index 263c82c..8f9003d 100644 --- a/src/main/resources/static/app.js +++ b/src/main/resources/static/app.js @@ -1,14 +1,12 @@ const stompClient = new StompJs.Client({ - brokerURL: 'ws://localhost:8080/stomp', + brokerURL: 'ws://localhost:8080/stomp' }); stompClient.onConnect = (frame) => { setConnected(true); console.log('Connected: ' + frame); - stompClient.subscribe('/sub/chat/hello', (greeting) => { + stompClient.subscribe('/sub/chat/aGVsbG86cG93ZXJhc3M=', (greeting) => { showGreeting(JSON.parse(greeting.body)); - },{ - Authorization: 'Basic aGVsbG86cG93ZXJhc3M=', }); }; @@ -44,29 +42,20 @@ function disconnect() { function sendName() { stompClient.publish({ - destination: "/pub/chat/hello", - body: JSON.stringify({'content': $("#name").val()}), - headers: { - Authorization: 'Basic aGVsbG86cG93ZXJhc3M=', - }, + destination: "/pub/chat/aGVsbG86cG93ZXJhc3M=", + body: JSON.stringify({'content': $("#name").val()}) }); } function enterRoom() { stompClient.publish({ - destination: "/pub/chat/hello/enter", - headers: { - Authorization: 'Basic aGVsbG86cG93ZXJhc3M=', - }, + destination: "/pub/chat/enter/aGVsbG86cG93ZXJhc3M=", }); } function leaveRoom() { stompClient.publish({ - destination: "/pub/chat/hello/leave", - headers: { - Authorization: 'Basic aGVsbG86cG93ZXJhc3M=', - }, + destination: "/pub/chat/leave/aGVsbG86cG93ZXJhc3M=", }); } From f3939b46264d26effe7af36b18baae1cdb2cdc0f Mon Sep 17 00:00:00 2001 From: thdwoqor Date: Thu, 26 Dec 2024 00:00:29 +0900 Subject: [PATCH 3/5] =?UTF-8?q?refactor:=20job=20skill=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/config/WebSocketConfig.java | 15 ++++-- .../interceptor/PathMatcherInterceptor.java | 47 ++++++++++--------- .../WebsocketPlayerArgumentResolver.java | 35 ++++++++++++++ .../mafiatogether/job/ui/JobController.java | 31 +++++++----- src/main/resources/static/app.js | 20 ++++++++ src/main/resources/static/index.html | 9 +++- 6 files changed, 118 insertions(+), 39 deletions(-) create mode 100644 src/main/java/mafia/mafiatogether/common/resolver/WebsocketPlayerArgumentResolver.java diff --git a/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java b/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java index 57a7d0e..c5435ba 100644 --- a/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java +++ b/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java @@ -1,10 +1,12 @@ package mafia.mafiatogether.common.config; - +import java.util.List; import lombok.RequiredArgsConstructor; +import mafia.mafiatogether.chat.ui.WebsocketPlayerArgumentResolver; import mafia.mafiatogether.common.interceptor.ChatInterceptor; import mafia.mafiatogether.common.interceptor.PathMatcherInterceptor; import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.messaging.simp.stomp.StompCommand; @@ -30,6 +32,11 @@ public void registerStompEndpoints(StompEndpointRegistry registry) { ); } + @Override + public void addArgumentResolvers(final List argumentResolvers) { + argumentResolvers.add(new WebsocketPlayerArgumentResolver()); + } + @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.setApplicationDestinationPrefixes("/pub"); @@ -40,9 +47,9 @@ public void configureMessageBroker(MessageBrokerRegistry registry) { public void configureClientInboundChannel(ChannelRegistration registry) { registry.interceptors( new PathMatcherInterceptor(chatInterceptor) - .includePathPattern("/chat/**", StompCommand.SUBSCRIBE) - .includePathPattern("/chat/**", StompCommand.DISCONNECT) - .includePathPattern("/chat/**", StompCommand.MESSAGE) + .includePathPattern("/sub/chat/**", StompCommand.SUBSCRIBE) + .includePathPattern("/pub/chat/**", StompCommand.MESSAGE) + .includePathPattern("/pub/chat/**", StompCommand.SEND) ); } diff --git a/src/main/java/mafia/mafiatogether/common/interceptor/PathMatcherInterceptor.java b/src/main/java/mafia/mafiatogether/common/interceptor/PathMatcherInterceptor.java index 83fed57..74c4b09 100644 --- a/src/main/java/mafia/mafiatogether/common/interceptor/PathMatcherInterceptor.java +++ b/src/main/java/mafia/mafiatogether/common/interceptor/PathMatcherInterceptor.java @@ -15,55 +15,56 @@ public class PathMatcherInterceptor implements ChannelInterceptor { private final ChannelInterceptor channelInterceptor; private final PathMatcher pathMatcher; - private final List includePathPattern; - private final List excludePathPattern; + private final List includePathPatterns; + private final List excludePathPatterns; - public PathMatcherInterceptor( - final ChannelInterceptor channelInterceptor - ) { + public PathMatcherInterceptor(final ChannelInterceptor channelInterceptor) { this.channelInterceptor = channelInterceptor; this.pathMatcher = new AntPathMatcher(); - this.includePathPattern = new ArrayList<>(); - this.excludePathPattern = new ArrayList<>(); + this.includePathPatterns = new ArrayList<>(); + this.excludePathPatterns = new ArrayList<>(); } - @Override public Message preSend(final Message message, final MessageChannel channel) { StompHeaderAccessor headerAccessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); - if ( + if (headerAccessor != null && headerAccessor.getDestination() != null && - notIncludedPath(headerAccessor.getDestination(), headerAccessor.getCommand()) - ) { + shouldIntercept(headerAccessor.getDestination(), headerAccessor.getCommand())) { return channelInterceptor.preSend(message, channel); } return ChannelInterceptor.super.preSend(message, channel); } - private boolean notIncludedPath(String destination, StompCommand command) { - boolean excludePattern = excludePathPattern.stream() - .anyMatch(stompMapping -> anyMatchPathPattern(destination, command, stompMapping)); + private boolean shouldIntercept(String destination, StompCommand command) { + boolean isExcluded = excludePathPatterns.stream() + .anyMatch(stompMapping -> matchesPathAndCommand(destination, command, stompMapping)); + + boolean isIncluded = includePathPatterns.stream() + .anyMatch(stompMapping -> matchesPathAndCommand(destination, command, stompMapping)); - boolean includePattern = includePathPattern.stream() - .anyMatch(stompMapping -> anyMatchPathPattern(destination, command, stompMapping)); + System.out.println("##"); + System.out.println("##"); + System.out.println(isExcluded); + System.out.println(isIncluded); - return excludePattern || !includePattern; + return isIncluded && !isExcluded; } - private boolean anyMatchPathPattern(String destination, StompCommand command, StompMapping stompMapping) { - return pathMatcher.match(destination, stompMapping.destination()) && + private boolean matchesPathAndCommand(String destination, StompCommand command, StompMapping stompMapping) { + return pathMatcher.match(stompMapping.destination(), destination) && stompMapping.command() == command; } - public PathMatcherInterceptor includePathPattern(String targetPath, StompCommand pathMethod) { - this.includePathPattern.add(new StompMapping(targetPath, pathMethod)); + public PathMatcherInterceptor includePathPattern(String targetPath, StompCommand command) { + this.includePathPatterns.add(new StompMapping(targetPath, command)); return this; } - public PathMatcherInterceptor excludePathPattern(String targetPath, StompCommand pathMethod) { - this.excludePathPattern.add(new StompMapping(targetPath, pathMethod)); + public PathMatcherInterceptor excludePathPattern(String targetPath, StompCommand command) { + this.excludePathPatterns.add(new StompMapping(targetPath, command)); return this; } diff --git a/src/main/java/mafia/mafiatogether/common/resolver/WebsocketPlayerArgumentResolver.java b/src/main/java/mafia/mafiatogether/common/resolver/WebsocketPlayerArgumentResolver.java new file mode 100644 index 0000000..fe86a1f --- /dev/null +++ b/src/main/java/mafia/mafiatogether/common/resolver/WebsocketPlayerArgumentResolver.java @@ -0,0 +1,35 @@ +package mafia.mafiatogether.chat.ui; + +import mafia.mafiatogether.common.annotation.PlayerInfo; +import mafia.mafiatogether.common.exception.AuthException; +import mafia.mafiatogether.common.exception.ExceptionCode; +import mafia.mafiatogether.common.resolver.PlayerInfoDto; +import mafia.mafiatogether.common.util.AuthExtractor; +import org.springframework.core.MethodParameter; +import org.springframework.messaging.Message; +import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; +import org.springframework.messaging.simp.SimpMessageHeaderAccessor; + +public class WebsocketPlayerArgumentResolver implements HandlerMethodArgumentResolver { + @Override + public boolean supportsParameter(final MethodParameter parameter) { + return parameter.hasParameterAnnotation(PlayerInfo.class); + } + + @Override + public Object resolveArgument(final MethodParameter parameter, final Message message) throws Exception { + SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.wrap(message); + String authorization = headerAccessor.getFirstNativeHeader("Authorization"); + if (authorization == null) { + throw new AuthException(ExceptionCode.MISSING_AUTHENTICATION_HEADER); + } + if (!authorization.startsWith("Basic")) { + throw new AuthException(ExceptionCode.MISSING_AUTHENTICATION_HEADER); + } + + String[] information = AuthExtractor.extractByAuthorization(authorization); + + return new PlayerInfoDto(information[0], information[1]); + } + +} diff --git a/src/main/java/mafia/mafiatogether/job/ui/JobController.java b/src/main/java/mafia/mafiatogether/job/ui/JobController.java index ea1f086..21c9c54 100644 --- a/src/main/java/mafia/mafiatogether/job/ui/JobController.java +++ b/src/main/java/mafia/mafiatogether/job/ui/JobController.java @@ -1,8 +1,10 @@ package mafia.mafiatogether.job.ui; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Base64; import lombok.RequiredArgsConstructor; -import mafia.mafiatogether.chat.annotation.SendToChatWithRedis; -import mafia.mafiatogether.chat.domain.Message; import mafia.mafiatogether.common.annotation.PlayerInfo; import mafia.mafiatogether.common.resolver.PlayerInfoDto; import mafia.mafiatogether.job.application.JobService; @@ -10,8 +12,8 @@ import mafia.mafiatogether.job.application.dto.response.JobExecuteAbilityResponse; import mafia.mafiatogether.job.application.dto.response.JobResponse; import mafia.mafiatogether.job.application.dto.response.JobResultResponse; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.http.ResponseEntity; -import org.springframework.messaging.handler.annotation.DestinationVariable; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.web.bind.annotation.GetMapping; @@ -25,6 +27,8 @@ @RequestMapping("/jobs") public class JobController { + private final StringRedisTemplate stringRedisTemplate; + private final ObjectMapper objectMapper; private final JobService jobService; @GetMapping("/my") @@ -32,15 +36,20 @@ public ResponseEntity getJob(@PlayerInfo PlayerInfoDto playerInfoDt return ResponseEntity.ok(jobService.getPlayerJob(playerInfoDto.code(), playerInfoDto.name())); } - @MessageMapping("/skill/{code}/{name}") - @SendToChatWithRedis("/sub/mafia/{code}") - public Message executeSkill( - @DestinationVariable("code") String code, - @DestinationVariable("name") String name, + @MessageMapping("/jobs/skill") + public void executeWebSocketSkill( + @PlayerInfo PlayerInfoDto playerInfoDto, @Payload JobExecuteAbilityRequest request - ) { - JobExecuteAbilityResponse response = jobService.executeSkill(code, name, request); - return Message.ofChat(response.job(), response.result()); + ) throws JsonProcessingException { + JobExecuteAbilityResponse response = jobService.executeSkill(playerInfoDto.code(), playerInfoDto.name(), + request); + String auth = Base64.getEncoder() + .encodeToString((playerInfoDto.code() + ":" + playerInfoDto.name()).getBytes()); + + stringRedisTemplate.convertAndSend( + String.format("/sub/job/skill/%s/%s", response.job().toLowerCase(), auth), + objectMapper.writeValueAsString(response) + ); } @PostMapping("/skill") diff --git a/src/main/resources/static/app.js b/src/main/resources/static/app.js index 8f9003d..af95ba3 100644 --- a/src/main/resources/static/app.js +++ b/src/main/resources/static/app.js @@ -8,6 +8,10 @@ stompClient.onConnect = (frame) => { stompClient.subscribe('/sub/chat/aGVsbG86cG93ZXJhc3M=', (greeting) => { showGreeting(JSON.parse(greeting.body)); }); + + stompClient.subscribe('/sub/job/skill/mafia/aGVsbG86cG93ZXJhc3M=', (greeting) => { + showSkill(JSON.parse(greeting.body)); + }); }; stompClient.onWebSocketError = (error) => { @@ -59,11 +63,26 @@ function leaveRoom() { }); } +function executeSkill() { + stompClient.publish({ + destination: "/pub/jobs/skill", + body: JSON.stringify({'target': $("#target").val()}), + headers: { + Authorization: 'Basic aGVsbG86cG93ZXJhc3M=', + }, + }); +} + function showGreeting(message) { console.log('Received: ' + JSON.stringify(message)); $("#greeting").append("" + JSON.stringify(message) + ""); } +function showSkill(message) { + console.log('Received: ' + JSON.stringify(message)); + $("#greeting").append("" + JSON.stringify(message) + ""); +} + $(function () { $("form").on('submit', (e) => e.preventDefault()); $("#connect").click(() => connect()); @@ -71,4 +90,5 @@ $(function () { $("#send").click(() => sendName()); $("#enter").click(() => enterRoom()); $("#leave").click(() => leaveRoom()); + $("#skill").click(() => executeSkill()); }); diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 7b86f54..b395080 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -34,6 +34,13 @@ +
+
+ + +
+ +
@@ -51,4 +58,4 @@
- \ No newline at end of file + From dae0ab446bc17132d66b677a236aa74ee440bc8a Mon Sep 17 00:00:00 2001 From: thdwoqor Date: Thu, 26 Dec 2024 00:18:55 +0900 Subject: [PATCH 4/5] =?UTF-8?q?refactor:=20auth=20=EC=9D=B8=EC=A6=9D?= =?UTF-8?q?=EB=B6=80=EB=B6=84=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/config/WebMvcConfig.java | 6 ++++- .../common/config/WebSocketConfig.java | 3 ++- .../interceptor/PathMatcherInterceptor.java | 5 ----- .../common/resolver/BasicAuthResolver.java | 22 +++++++++++++++++++ .../resolver/PlayerArgumentResolver.java | 14 ++++++------ .../WebsocketPlayerArgumentResolver.java | 20 ++++++++--------- 6 files changed, 45 insertions(+), 25 deletions(-) create mode 100644 src/main/java/mafia/mafiatogether/common/resolver/BasicAuthResolver.java diff --git a/src/main/java/mafia/mafiatogether/common/config/WebMvcConfig.java b/src/main/java/mafia/mafiatogether/common/config/WebMvcConfig.java index 988dd51..6023c3f 100644 --- a/src/main/java/mafia/mafiatogether/common/config/WebMvcConfig.java +++ b/src/main/java/mafia/mafiatogether/common/config/WebMvcConfig.java @@ -1,5 +1,6 @@ package mafia.mafiatogether.common.config; +import lombok.RequiredArgsConstructor; import mafia.mafiatogether.common.resolver.PlayerArgumentResolver; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpHeaders; @@ -10,11 +11,14 @@ import java.util.List; @Configuration +@RequiredArgsConstructor public class WebMvcConfig implements WebMvcConfigurer { + private final PlayerArgumentResolver playerArgumentResolver; + @Override public void addArgumentResolvers(final List resolvers) { - resolvers.add(new PlayerArgumentResolver()); + resolvers.add(playerArgumentResolver); } @Override diff --git a/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java b/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java index c5435ba..9ba3d96 100644 --- a/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java +++ b/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java @@ -20,6 +20,7 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { private final ChatInterceptor chatInterceptor; + private final WebsocketPlayerArgumentResolver websocketPlayerArgumentResolver; @Override public void registerStompEndpoints(StompEndpointRegistry registry) { @@ -34,7 +35,7 @@ public void registerStompEndpoints(StompEndpointRegistry registry) { @Override public void addArgumentResolvers(final List argumentResolvers) { - argumentResolvers.add(new WebsocketPlayerArgumentResolver()); + argumentResolvers.add(websocketPlayerArgumentResolver); } @Override diff --git a/src/main/java/mafia/mafiatogether/common/interceptor/PathMatcherInterceptor.java b/src/main/java/mafia/mafiatogether/common/interceptor/PathMatcherInterceptor.java index 74c4b09..f9e86d3 100644 --- a/src/main/java/mafia/mafiatogether/common/interceptor/PathMatcherInterceptor.java +++ b/src/main/java/mafia/mafiatogether/common/interceptor/PathMatcherInterceptor.java @@ -45,11 +45,6 @@ private boolean shouldIntercept(String destination, StompCommand command) { boolean isIncluded = includePathPatterns.stream() .anyMatch(stompMapping -> matchesPathAndCommand(destination, command, stompMapping)); - System.out.println("##"); - System.out.println("##"); - System.out.println(isExcluded); - System.out.println(isIncluded); - return isIncluded && !isExcluded; } diff --git a/src/main/java/mafia/mafiatogether/common/resolver/BasicAuthResolver.java b/src/main/java/mafia/mafiatogether/common/resolver/BasicAuthResolver.java new file mode 100644 index 0000000..d055219 --- /dev/null +++ b/src/main/java/mafia/mafiatogether/common/resolver/BasicAuthResolver.java @@ -0,0 +1,22 @@ +package mafia.mafiatogether.common.resolver; + +import lombok.RequiredArgsConstructor; +import mafia.mafiatogether.common.exception.AuthException; +import mafia.mafiatogether.common.exception.ExceptionCode; +import mafia.mafiatogether.common.util.AuthExtractor; +import org.springframework.stereotype.Component; + +@Component +public class BasicAuthResolver { + + public String[] resolve(final String authorization) { + if (authorization == null) { + throw new AuthException(ExceptionCode.MISSING_AUTHENTICATION_HEADER); + } + if (!authorization.startsWith("Basic")) { + throw new AuthException(ExceptionCode.MISSING_AUTHENTICATION_HEADER); + } + + return AuthExtractor.extractByAuthorization(authorization); + } +} diff --git a/src/main/java/mafia/mafiatogether/common/resolver/PlayerArgumentResolver.java b/src/main/java/mafia/mafiatogether/common/resolver/PlayerArgumentResolver.java index 958b844..2eac36d 100644 --- a/src/main/java/mafia/mafiatogether/common/resolver/PlayerArgumentResolver.java +++ b/src/main/java/mafia/mafiatogether/common/resolver/PlayerArgumentResolver.java @@ -1,18 +1,23 @@ package mafia.mafiatogether.common.resolver; import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; import mafia.mafiatogether.common.annotation.PlayerInfo; import mafia.mafiatogether.common.exception.AuthException; import mafia.mafiatogether.common.exception.ExceptionCode; -import mafia.mafiatogether.common.util.AuthExtractor; import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; +@Component +@RequiredArgsConstructor public class PlayerArgumentResolver implements HandlerMethodArgumentResolver { + private final BasicAuthResolver basicAuthResolver; + @Override public boolean supportsParameter(final MethodParameter parameter) { return parameter.hasParameterAnnotation(PlayerInfo.class); @@ -22,17 +27,12 @@ public boolean supportsParameter(final MethodParameter parameter) { public PlayerInfoDto resolveArgument(final MethodParameter parameter, final ModelAndViewContainer mavContainer, final NativeWebRequest webRequest, final WebDataBinderFactory binderFactory) { HttpServletRequest httpServletRequest = webRequest.getNativeRequest(HttpServletRequest.class); - if (httpServletRequest == null) { throw new AuthException(ExceptionCode.NOT_FOUND_REQUEST); } String authorization = httpServletRequest.getHeader("Authorization"); - if (authorization == null) { - throw new AuthException(ExceptionCode.MISSING_AUTHENTICATION_HEADER); - } - - String[] information = AuthExtractor.extractByAuthorization(authorization); + String[] information = basicAuthResolver.resolve(authorization); return new PlayerInfoDto(information[0], information[1]); } } diff --git a/src/main/java/mafia/mafiatogether/common/resolver/WebsocketPlayerArgumentResolver.java b/src/main/java/mafia/mafiatogether/common/resolver/WebsocketPlayerArgumentResolver.java index fe86a1f..338e857 100644 --- a/src/main/java/mafia/mafiatogether/common/resolver/WebsocketPlayerArgumentResolver.java +++ b/src/main/java/mafia/mafiatogether/common/resolver/WebsocketPlayerArgumentResolver.java @@ -1,16 +1,21 @@ package mafia.mafiatogether.chat.ui; +import lombok.RequiredArgsConstructor; import mafia.mafiatogether.common.annotation.PlayerInfo; -import mafia.mafiatogether.common.exception.AuthException; -import mafia.mafiatogether.common.exception.ExceptionCode; +import mafia.mafiatogether.common.resolver.BasicAuthResolver; import mafia.mafiatogether.common.resolver.PlayerInfoDto; -import mafia.mafiatogether.common.util.AuthExtractor; import org.springframework.core.MethodParameter; import org.springframework.messaging.Message; import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; import org.springframework.messaging.simp.SimpMessageHeaderAccessor; +import org.springframework.stereotype.Component; +@Component +@RequiredArgsConstructor public class WebsocketPlayerArgumentResolver implements HandlerMethodArgumentResolver { + + private final BasicAuthResolver basicAuthResolver; + @Override public boolean supportsParameter(final MethodParameter parameter) { return parameter.hasParameterAnnotation(PlayerInfo.class); @@ -20,14 +25,7 @@ public boolean supportsParameter(final MethodParameter parameter) { public Object resolveArgument(final MethodParameter parameter, final Message message) throws Exception { SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.wrap(message); String authorization = headerAccessor.getFirstNativeHeader("Authorization"); - if (authorization == null) { - throw new AuthException(ExceptionCode.MISSING_AUTHENTICATION_HEADER); - } - if (!authorization.startsWith("Basic")) { - throw new AuthException(ExceptionCode.MISSING_AUTHENTICATION_HEADER); - } - - String[] information = AuthExtractor.extractByAuthorization(authorization); + String[] information = basicAuthResolver.resolve(authorization); return new PlayerInfoDto(information[0], information[1]); } From 3847b2a1befa2614533bb9b56e759362b92381da Mon Sep 17 00:00:00 2001 From: waterricecake Date: Thu, 26 Dec 2024 15:26:42 +0900 Subject: [PATCH 5/5] =?UTF-8?q?fix:=20WebsocketPlayerArgumentResolver=20?= =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=20=EC=9C=84=EC=B9=98=20=EC=88=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mafia/mafiatogether/common/config/WebSocketConfig.java | 2 +- .../common/resolver/WebsocketPlayerArgumentResolver.java | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java b/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java index 9ba3d96..6599d16 100644 --- a/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java +++ b/src/main/java/mafia/mafiatogether/common/config/WebSocketConfig.java @@ -2,9 +2,9 @@ import java.util.List; import lombok.RequiredArgsConstructor; -import mafia.mafiatogether.chat.ui.WebsocketPlayerArgumentResolver; import mafia.mafiatogether.common.interceptor.ChatInterceptor; import mafia.mafiatogether.common.interceptor.PathMatcherInterceptor; +import mafia.mafiatogether.common.resolver.WebsocketPlayerArgumentResolver; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; import org.springframework.messaging.simp.config.ChannelRegistration; diff --git a/src/main/java/mafia/mafiatogether/common/resolver/WebsocketPlayerArgumentResolver.java b/src/main/java/mafia/mafiatogether/common/resolver/WebsocketPlayerArgumentResolver.java index 338e857..c35d443 100644 --- a/src/main/java/mafia/mafiatogether/common/resolver/WebsocketPlayerArgumentResolver.java +++ b/src/main/java/mafia/mafiatogether/common/resolver/WebsocketPlayerArgumentResolver.java @@ -1,9 +1,7 @@ -package mafia.mafiatogether.chat.ui; +package mafia.mafiatogether.common.resolver; import lombok.RequiredArgsConstructor; import mafia.mafiatogether.common.annotation.PlayerInfo; -import mafia.mafiatogether.common.resolver.BasicAuthResolver; -import mafia.mafiatogether.common.resolver.PlayerInfoDto; import org.springframework.core.MethodParameter; import org.springframework.messaging.Message; import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;