-
Notifications
You must be signed in to change notification settings - Fork 0
[FEAT] 웹 소켓 기반 시그널링 서버 초안을 구축한다. #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| package com.focussu.backend.signalling; | ||
|
|
||
| import com.focussu.backend.auth.util.JwtTokenUtil; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.http.HttpHeaders; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.http.server.ServerHttpRequest; | ||
| import org.springframework.http.server.ServerHttpResponse; | ||
| import org.springframework.web.socket.WebSocketHandler; | ||
| import org.springframework.web.socket.server.HandshakeInterceptor; | ||
| import org.springframework.web.util.UriComponentsBuilder; | ||
|
|
||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| @Slf4j | ||
| public class JwtHandshakeInterceptor implements HandshakeInterceptor { | ||
| private final JwtTokenUtil jwtTokenUtil; | ||
|
|
||
| public JwtHandshakeInterceptor(JwtTokenUtil jwtTokenUtil) { | ||
| this.jwtTokenUtil = jwtTokenUtil; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean beforeHandshake(ServerHttpRequest request, | ||
| ServerHttpResponse response, | ||
| WebSocketHandler handler, | ||
| Map<String, Object> attrs) throws Exception { | ||
| // 1) 헤더에서 Authorization 꺼내기 | ||
| List<String> authHeaders = request.getHeaders().get(HttpHeaders.AUTHORIZATION); | ||
| String token = null; | ||
| if (authHeaders != null && !authHeaders.isEmpty() | ||
| && authHeaders.get(0).startsWith("Bearer ")) { | ||
| token = authHeaders.get(0).substring(7); | ||
| } | ||
|
|
||
| // 2) 헤더에 토큰이 없으면 query param 으로 fallback | ||
| if (token == null) { | ||
| token = UriComponentsBuilder.fromUri(request.getURI()) | ||
| .build() | ||
| .getQueryParams() | ||
| .getFirst("token"); | ||
| } | ||
|
|
||
| // 3) 로깅 | ||
| log.info("[HandshakeInterceptor] 요청 URI: {}", request.getURI()); | ||
| log.info("[HandshakeInterceptor] 토큰: {}", token); | ||
|
|
||
| // 4) 검증 | ||
| if (token == null || !jwtTokenUtil.validateToken(token)) { | ||
| response.setStatusCode(HttpStatus.UNAUTHORIZED); | ||
| return false; | ||
| } | ||
|
|
||
| attrs.put("userId", jwtTokenUtil.getUsernameFromToken(token)); | ||
| return true; | ||
| } | ||
|
|
||
|
|
||
| @Override | ||
| public void afterHandshake(ServerHttpRequest request, | ||
| ServerHttpResponse response, | ||
| WebSocketHandler wsHandler, | ||
| Exception exception) { | ||
| // 아무 처리 필요 없으면 빈 바디로 둡니다. | ||
| // // 또는 디버깅용: | ||
| System.out.println("WebSocket Handshake 완료: " + request.getRemoteAddress()); | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,123 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package com.focussu.backend.signalling; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.fasterxml.jackson.databind.node.ObjectNode; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.AllArgsConstructor; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.NoArgsConstructor; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.extern.slf4j.Slf4j; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.web.socket.CloseStatus; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.web.socket.TextMessage; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.web.socket.WebSocketSession; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.web.socket.handler.TextWebSocketHandler; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.io.IOException; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Collections; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Map; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Set; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.concurrent.ConcurrentHashMap; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Slf4j | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public class SignalingHandler extends TextWebSocketHandler { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 사용자ID → WebSocketSession 맵 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 방ID → 사용자ID 세트 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final Map<String, Set<String>> rooms = new ConcurrentHashMap<>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final ObjectMapper mapper = new ObjectMapper(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void afterConnectionEstablished(WebSocketSession session) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String userId = (String) session.getAttributes().get("userId"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sessions.put(userId, session); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+29
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. userId null 체크 추가가 필요합니다. 세션 속성에서 userId를 가져올 때 null일 가능성을 고려해야 합니다. 다음과 같이 null 체크를 추가하세요: - String userId = (String) session.getAttributes().get("userId");
- sessions.put(userId, session);
+ String userId = (String) session.getAttributes().get("userId");
+ if (userId == null) {
+ log.warn("[Signaling] Connection rejected: userId not found in session attributes");
+ session.close();
+ return;
+ }
+ sessions.put(userId, session);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.info("[Signaling] CONNECTED: {}", userId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| protected void handleTextMessage(WebSocketSession session, TextMessage msg) throws IOException { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SignalingMessage in = mapper.readValue(msg.getPayload(), SignalingMessage.class); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String from = (String) session.getAttributes().get("userId"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String roomId = in.getRoomId(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.info("[Signaling] [{}] {} ▶ {}", roomId, from, in.getType()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+35
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. JSON 파싱 예외 처리 및 입력 검증이 필요합니다. 메시지 파싱 시 예외 처리와 기본적인 검증이 부족합니다. 다음과 같이 예외 처리와 검증을 추가하세요: - SignalingMessage in = mapper.readValue(msg.getPayload(), SignalingMessage.class);
- String from = (String) session.getAttributes().get("userId");
- String roomId = in.getRoomId();
+ SignalingMessage in;
+ try {
+ in = mapper.readValue(msg.getPayload(), SignalingMessage.class);
+ } catch (IOException e) {
+ log.warn("[Signaling] Invalid message format from {}: {}", session.getId(), e.getMessage());
+ return;
+ }
+
+ String from = (String) session.getAttributes().get("userId");
+ if (from == null) {
+ log.warn("[Signaling] No userId in session attributes");
+ return;
+ }
+
+ String roomId = in.getRoomId();
+ if (roomId == null || roomId.trim().isEmpty()) {
+ log.warn("[Signaling] Invalid roomId from {}", from);
+ return;
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| switch (in.getType()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "join": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // (1) 방 가입 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rooms.computeIfAbsent(roomId, r -> ConcurrentHashMap.newKeySet()).add(from); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // (2) 기존 멤버에게 new-peer 브로드캐스트 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ObjectNode joinPayload = mapper.createObjectNode().put("from", from); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SignalingMessage newPeerMsg = new SignalingMessage("new-peer", roomId, null, joinPayload); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| broadcast(roomId, newPeerMsg, from); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "leave": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // (1) 방 탈퇴 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Set<String> members = rooms.getOrDefault(roomId, Collections.emptySet()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| members.remove(from); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // (2) peer-left 방송 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ObjectNode leavePayload = mapper.createObjectNode().put("from", from); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SignalingMessage leftMsg = new SignalingMessage("peer-left", roomId, null, leavePayload); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| broadcast(roomId, leftMsg, from); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+53
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 동시성 문제 가능성이 있습니다. 방에서 사용자를 제거할 때 동시성 문제가 발생할 수 있습니다. 다음과 같이 안전하게 수정하세요: - Set<String> members = rooms.getOrDefault(roomId, Collections.emptySet());
- members.remove(from);
+ rooms.computeIfPresent(roomId, (k, members) -> {
+ members.remove(from);
+ return members.isEmpty() ? null : members;
+ });📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "offer": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "answer": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "candidate": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "ping": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 1:1 대상 있으면 sendTo, 없으면 룸 브로드캐스트 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (in.getTo() != null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sendTo(roomId, in.getTo(), from, in); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| broadcast(roomId, in, from); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.warn("[Signaling] Unknown message type: {}", in.getType()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String userId = (String) session.getAttributes().get("userId"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sessions.remove(userId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rooms.values().forEach(m -> m.remove(userId)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.info("[Signaling] DISCONNECTED: {}", userId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+80
to
+83
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 연결 종료 시 userId null 체크가 필요합니다. 연결 종료 시에도 userId가 null일 가능성을 고려해야 합니다. 다음과 같이 null 체크를 추가하세요: String userId = (String) session.getAttributes().get("userId");
+ if (userId == null) {
+ log.warn("[Signaling] Cannot cleanup: userId not found in session attributes");
+ return;
+ }
sessions.remove(userId);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 룸 내 전체 브로드캐스트 (sender 제외) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void broadcast(String roomId, SignalingMessage msg, String sender) throws IOException { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (msg.getPayload() == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| msg.setPayload(mapper.createObjectNode()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ((ObjectNode) msg.getPayload()).put("from", sender); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+90
to
+93
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 메시지 객체 변조 문제가 있습니다. 원본 메시지 객체의 payload를 직접 수정하고 있어 예상치 못한 부작용이 발생할 수 있습니다. 메시지를 복사한 후 수정하도록 변경하세요: - if (msg.getPayload() == null) {
- msg.setPayload(mapper.createObjectNode());
- }
- ((ObjectNode) msg.getPayload()).put("from", sender);
+ SignalingMessage msgCopy = new SignalingMessage(msg.getType(), msg.getRoomId(), msg.getTo(),
+ msg.getPayload() != null ? msg.getPayload().deepCopy() : mapper.createObjectNode());
+ ((ObjectNode) msgCopy.getPayload()).put("from", sender);그리고 이후 코드에서 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (String to : rooms.getOrDefault(roomId, Collections.emptySet())) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!to.equals(sender)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WebSocketSession ws = sessions.get(to); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (ws != null && ws.isOpen()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ws.sendMessage(new TextMessage(mapper.writeValueAsString(msg))); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.info("[Signaling] BROADCAST to room {}: {}", roomId, msg.getType()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 룸 내 특정 유저에게 전송 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void sendTo(String roomId, String to, String from, SignalingMessage msg) throws IOException { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (msg.getPayload() == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| msg.setPayload(mapper.createObjectNode()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ((ObjectNode) msg.getPayload()).put("from", from); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+110
to
+113
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion sendTo 메서드에서도 동일한 메시지 변조 문제가 있습니다. broadcast 메서드와 동일한 문제입니다. 동일하게 메시지를 복사한 후 수정하도록 변경하세요: - if (msg.getPayload() == null) {
- msg.setPayload(mapper.createObjectNode());
- }
- ((ObjectNode) msg.getPayload()).put("from", from);
+ SignalingMessage msgCopy = new SignalingMessage(msg.getType(), msg.getRoomId(), msg.getTo(),
+ msg.getPayload() != null ? msg.getPayload().deepCopy() : mapper.createObjectNode());
+ ((ObjectNode) msgCopy.getPayload()).put("from", from);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WebSocketSession ws = sessions.get(to); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (ws != null && ws.isOpen() && rooms.getOrDefault(roomId, Collections.emptySet()).contains(to)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ws.sendMessage(new TextMessage(mapper.writeValueAsString(msg))); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.info("[Signaling] SEND to {} in room {}: {}", to, roomId, msg.getType()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.warn("[Signaling] Cannot send to {} (not in room or closed)", to); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package com.focussu.backend.signalling; | ||
|
|
||
| import com.fasterxml.jackson.databind.JsonNode; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Data; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| @Data | ||
| @NoArgsConstructor | ||
| @AllArgsConstructor | ||
| public class SignalingMessage { | ||
| private String type; // "join"|"leave"|"offer"|"answer"|"candidate" | ||
| private String roomId; // ex: "room-1234" | ||
| private String to; // 1:1용 수신자 userId (필요 시) | ||
| private JsonNode payload; | ||
|
|
||
| public SignalingMessage(String type, String roomId, JsonNode payload) { | ||
| this(type, roomId, null, payload); | ||
| } | ||
| // getters/setters... | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package com.focussu.backend.signalling; | ||
|
|
||
| import org.springframework.context.annotation.Bean; | ||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; | ||
|
|
||
| @Configuration | ||
| public class WebSecurityIgnoreConfig { | ||
| @Bean | ||
| public WebSecurityCustomizer webSecurityCustomizer() { | ||
| return (web) -> web.ignoring() | ||
| .requestMatchers("/ws/signaling"); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,32 @@ | ||||||||||||||||||||||
| package com.focussu.backend.signalling; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import com.focussu.backend.auth.util.JwtTokenUtil; | ||||||||||||||||||||||
| import org.springframework.context.annotation.Bean; | ||||||||||||||||||||||
| import org.springframework.context.annotation.Configuration; | ||||||||||||||||||||||
| import org.springframework.web.socket.WebSocketHandler; | ||||||||||||||||||||||
| import org.springframework.web.socket.config.annotation.EnableWebSocket; | ||||||||||||||||||||||
| import org.springframework.web.socket.config.annotation.WebSocketConfigurer; | ||||||||||||||||||||||
| import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @Configuration | ||||||||||||||||||||||
| @EnableWebSocket | ||||||||||||||||||||||
| public class WebSocketConfig implements WebSocketConfigurer { | ||||||||||||||||||||||
| private final JwtTokenUtil jwtTokenUtil; // 기존에 쓰시던 유틸 | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| public WebSocketConfig(JwtTokenUtil jwtTokenUtil) { | ||||||||||||||||||||||
| this.jwtTokenUtil = jwtTokenUtil; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @Override | ||||||||||||||||||||||
| public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { | ||||||||||||||||||||||
| registry | ||||||||||||||||||||||
| .addHandler(signalingHandler(), "/ws/signaling") | ||||||||||||||||||||||
| .setAllowedOrigins("*") | ||||||||||||||||||||||
| .addInterceptors(new JwtHandshakeInterceptor(jwtTokenUtil)); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
Comment on lines
+22
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion CORS 설정을 프로덕션 환경에 맞게 제한하세요.
- .setAllowedOrigins("*")
+ .setAllowedOrigins("https://yourdomain.com", "https://localhost:3000")📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @Bean | ||||||||||||||||||||||
| public WebSocketHandler signalingHandler() { | ||||||||||||||||||||||
| return new SignalingHandler(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
보안상 민감한 토큰 정보를 로그에 기록하지 마세요.
JWT 토큰을 로그에 기록하는 것은 보안 위험을 초래할 수 있습니다. 토큰 존재 여부만 로깅하거나 마스킹 처리를 권장합니다.
// 3) 로깅 log.info("[HandshakeInterceptor] 요청 URI: {}", request.getURI()); - log.info("[HandshakeInterceptor] 토큰: {}", token); + log.info("[HandshakeInterceptor] 토큰 존재 여부: {}", token != null);📝 Committable suggestion
🤖 Prompt for AI Agents