Skip to content
Merged
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
4 changes: 4 additions & 0 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ dependencies {
// h2 database(for test)
testImplementation 'com.h2database:h2:2.2.220'

// signalling
implementation 'org.springframework.boot:spring-boot-starter-websocket'
implementation 'com.fasterxml.jackson.core:jackson-databind'

// test
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenUtil jwtTokenUtil;
private final TokenService tokenService;

@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
// 1) WebSocket 핸드쉐이크는 GET + Upgrade:websocket

// 2) 이 외에도 필요하다면 특정 경로를 추가로 제외할 수 있습니다.
return "GET".equalsIgnoreCase(request.getMethod()) &&
"websocket".equalsIgnoreCase(request.getHeader("Upgrade")) &&
request.getRequestURI().equals("/ws/signaling");
}

@Override
protected void doFilterInternal(HttpServletRequest req,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,15 @@ public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}

public boolean validateToken(String token) {
try {
// 서명 검증 및 Claims 파싱
getAllClaimsFromToken(token);
// 만료 여부 검사
return !isTokenExpired(token);
} catch (AuthException e) {
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.requestMatchers(WhiteList.DOCS.getPatterns()).permitAll()
.requestMatchers(WhiteList.AUTH.getPatterns()).permitAll()
.requestMatchers(WhiteList.CHECKER.getPatterns()).permitAll()
.requestMatchers("/ws/signaling").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(authExceptionFilter, UsernamePasswordAuthenticationFilter.class) // 예외 처리
Expand Down
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);

Comment on lines +45 to +48
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

보안상 민감한 토큰 정보를 로그에 기록하지 마세요.

JWT 토큰을 로그에 기록하는 것은 보안 위험을 초래할 수 있습니다. 토큰 존재 여부만 로깅하거나 마스킹 처리를 권장합니다.

         // 3) 로깅
         log.info("[HandshakeInterceptor] 요청 URI: {}", request.getURI());
-        log.info("[HandshakeInterceptor] 토큰: {}", token);
+        log.info("[HandshakeInterceptor] 토큰 존재 여부: {}", token != null);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 3) 로깅
log.info("[HandshakeInterceptor] 요청 URI: {}", request.getURI());
log.info("[HandshakeInterceptor] 토큰: {}", token);
// 3) 로깅
log.info("[HandshakeInterceptor] 요청 URI: {}", request.getURI());
log.info("[HandshakeInterceptor] 토큰 존재 여부: {}", token != null);
🤖 Prompt for AI Agents
In
backend/src/main/java/com/focussu/backend/signalling/JwtHandshakeInterceptor.java
around lines 45 to 48, the JWT token is being logged in plain text, which poses
a security risk. Modify the logging to avoid printing the full token; instead,
log only whether the token exists or mask part of the token to prevent exposing
sensitive information.

// 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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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);
🤖 Prompt for AI Agents
In backend/src/main/java/com/focussu/backend/signalling/SignalingHandler.java
around lines 29 to 30, add a null check for userId after retrieving it from
session attributes to prevent potential NullPointerException. If userId is null,
handle it appropriately, such as by skipping adding to sessions or logging a
warning, before calling sessions.put(userId, session).

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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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());
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();
+ 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;
+ }
log.info("[Signaling] [{}] {} ▶ {}", roomId, from, in.getType());
🤖 Prompt for AI Agents
In backend/src/main/java/com/focussu/backend/signalling/SignalingHandler.java
around lines 35 to 39, the JSON parsing of the incoming message lacks exception
handling and input validation. Wrap the mapper.readValue call in a try-catch
block to handle potential parsing exceptions, and add checks to validate that
the parsed SignalingMessage and its critical fields like roomId and type are not
null or invalid before proceeding. Log or handle errors appropriately to prevent
the method from failing silently or crashing.


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
Copy link

Choose a reason for hiding this comment

The 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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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);
rooms.computeIfPresent(roomId, (k, members) -> {
members.remove(from);
return members.isEmpty() ? null : members;
});
// (2) peer-left 방송
ObjectNode leavePayload = mapper.createObjectNode().put("from", from);
SignalingMessage leftMsg = new SignalingMessage("peer-left", roomId, null, leavePayload);
broadcast(roomId, leftMsg, from);
🤖 Prompt for AI Agents
In backend/src/main/java/com/focussu/backend/signalling/SignalingHandler.java
around lines 53 to 58, the code removes a user from the room's member set
without synchronization, which can cause concurrency issues. To fix this, ensure
thread-safe access by synchronizing on the room's member set or using a
concurrent collection when modifying the members set. This will prevent
concurrent modification exceptions and maintain data consistency.

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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

연결 종료 시 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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
String userId = (String) session.getAttributes().get("userId");
sessions.remove(userId);
rooms.values().forEach(m -> m.remove(userId));
log.info("[Signaling] DISCONNECTED: {}", userId);
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);
rooms.values().forEach(m -> m.remove(userId));
log.info("[Signaling] DISCONNECTED: {}", userId);
🤖 Prompt for AI Agents
In backend/src/main/java/com/focussu/backend/signalling/SignalingHandler.java
around lines 80 to 83, add a null check for userId before using it. The current
code assumes userId is always non-null, but it can be null on connection
termination. Fix this by checking if userId is not null before removing it from
sessions and rooms, and before logging the disconnection event.

}

/**
* 룸 내 전체 브로드캐스트 (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
Copy link

Choose a reason for hiding this comment

The 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);

그리고 이후 코드에서 msg 대신 msgCopy를 사용하세요.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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);
🤖 Prompt for AI Agents
In backend/src/main/java/com/focussu/backend/signalling/SignalingHandler.java
around lines 90 to 93, the code directly modifies the original message object's
payload, which can cause unintended side effects. To fix this, create a deep
copy of the message object before making any changes, then modify the payload on
this copied object. Replace all subsequent uses of the original message variable
with the new copied message variable to avoid mutating the original message.


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
Copy link

Choose a reason for hiding this comment

The 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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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);
🤖 Prompt for AI Agents
In backend/src/main/java/com/focussu/backend/signalling/SignalingHandler.java
around lines 110 to 113, the sendTo method modifies the original message payload
directly, causing unintended side effects similar to the broadcast method. To
fix this, create a copy of the message payload before making any modifications,
then update the copy with the "from" field and use this modified copy instead of
altering the original message.


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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

CORS 설정을 프로덕션 환경에 맞게 제한하세요.

setAllowedOrigins("*")는 개발 환경에서는 편리하지만 프로덕션에서는 보안 위험이 있습니다. 실제 프론트엔드 도메인으로 제한하는 것을 권장합니다.

-                .setAllowedOrigins("*")
+                .setAllowedOrigins("https://yourdomain.com", "https://localhost:3000")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
registry
.addHandler(signalingHandler(), "/ws/signaling")
.setAllowedOrigins("*")
.addInterceptors(new JwtHandshakeInterceptor(jwtTokenUtil));
}
registry
.addHandler(signalingHandler(), "/ws/signaling")
.setAllowedOrigins("https://yourdomain.com", "https://localhost:3000")
.addInterceptors(new JwtHandshakeInterceptor(jwtTokenUtil));
}
🤖 Prompt for AI Agents
In backend/src/main/java/com/focussu/backend/signalling/WebSocketConfig.java
around lines 22 to 26, the CORS setting uses setAllowedOrigins("*"), which is
insecure for production. Replace the wildcard "*" with the specific frontend
domain(s) allowed to connect, ensuring only trusted origins can access the
WebSocket endpoint.


@Bean
public WebSocketHandler signalingHandler() {
return new SignalingHandler();
}
}
2 changes: 1 addition & 1 deletion backend/src/main/resources/http/auth.http
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ POST http://localhost:8080/auth/login
Content-Type: application/json

{
"email": "test@gmail.com",
"email": "test3@gmail.com",
"password": "testpassword"
}

Expand Down
4 changes: 2 additions & 2 deletions backend/src/main/resources/http/member.http
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ POST http://localhost:8080/api/members
Content-Type: application/json

{
"name": "test",
"email": "test@gmail.com",
"name": "test3",
"email": "test3@gmail.com",
"password": "testpassword"
}
Loading