-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: EunJiJung <bianbbc87@gmail.com>
- Loading branch information
Showing
10 changed files
with
431 additions
and
280 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
...server/src/main/java/com/asyncgate/signaling_server/controller/StompWebRtcController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package com.asyncgate.signaling_server.controller; | ||
|
||
import com.asyncgate.signaling_server.dto.request.KurentoOfferRequest; | ||
import com.asyncgate.signaling_server.signaling.KurentoManager; | ||
import lombok.RequiredArgsConstructor; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.messaging.handler.annotation.MessageMapping; | ||
import org.springframework.messaging.simp.SimpMessagingTemplate; | ||
import org.springframework.messaging.simp.stomp.StompHeaderAccessor; | ||
import org.springframework.stereotype.Controller; | ||
|
||
@Controller | ||
@RequiredArgsConstructor | ||
public class StompWebRtcController { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(StompWebRtcController.class); | ||
private final SimpMessagingTemplate messagingTemplate; | ||
private final KurentoManager kurentoManager; | ||
|
||
/** | ||
* WebRTC Offer 메시지 처리 | ||
*/ | ||
@MessageMapping("/offer") | ||
public void handleOffer(KurentoOfferRequest message, StompHeaderAccessor accessor) { | ||
// 메시지 처리 및 Kurento에서 사용자 연결 | ||
kurentoManager.processSdpOffer(message, accessor); | ||
|
||
kurentoManager.startIceCandidateListener(message, accessor); | ||
} | ||
|
||
/** | ||
* ICE Candidate 메시지 처리 | ||
*/ | ||
@MessageMapping("/candidate") | ||
public void handleIceCandidate(KurentoOfferRequest message, StompHeaderAccessor accessor) { | ||
|
||
// Kurento에 ICE Candidate 전달 | ||
kurentoManager.addIceCandidates(message, accessor); | ||
} | ||
|
||
/** | ||
* 미디어 토글 (AUDIO, MEDIA, DATA) | ||
*/ | ||
@MessageMapping("/toggle") | ||
public void toggleMediaState(KurentoOfferRequest message, StompHeaderAccessor accessor) { | ||
kurentoManager.updateUserMediaState(message, accessor); | ||
} | ||
|
||
/** | ||
* 유저 정보 반환 | ||
*/ | ||
@MessageMapping("/users") | ||
public void getUsersInRoom(KurentoOfferRequest message, StompHeaderAccessor accessor) { | ||
kurentoManager.getUsersInChannel(message); | ||
} | ||
|
||
/** | ||
* WebRTC 종료 처리 | ||
*/ | ||
@MessageMapping("/exit") | ||
public void handleExit(KurentoOfferRequest message, StompHeaderAccessor accessor) { | ||
// Kurento에서 사용자 제거 | ||
kurentoManager.removeUserFromChannel(message, accessor); | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
...-server/src/main/java/com/asyncgate/signaling_server/dto/request/KurentoOfferRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.asyncgate.signaling_server.dto.request; | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
import org.kurento.client.IceCandidate; | ||
import org.springframework.lang.Nullable; | ||
|
||
|
||
public record KurentoOfferRequest( | ||
|
||
@JsonProperty("type") | ||
String type, | ||
|
||
@JsonProperty("data") | ||
KurentoOfferData data | ||
) { | ||
public record KurentoOfferData( | ||
@JsonProperty("room_id") | ||
String roomId, | ||
|
||
@Nullable | ||
@JsonProperty("sdp_offer") | ||
String sdpOffer, | ||
|
||
@Nullable | ||
@JsonProperty("candidate") | ||
IceCandidate candidate, | ||
|
||
@Nullable | ||
@JsonProperty("enabled") | ||
boolean enabled | ||
) {} | ||
} |
21 changes: 21 additions & 0 deletions
21
...rver/src/main/java/com/asyncgate/signaling_server/dto/response/KurentoAnswerResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.asyncgate.signaling_server.dto.response; | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
public class KurentoAnswerResponse { | ||
|
||
@JsonProperty("type") | ||
private String type; | ||
|
||
@JsonProperty("message") | ||
private String message; | ||
|
||
@Builder | ||
public KurentoAnswerResponse(String type, String message) { | ||
this.type = type; | ||
this.message = message; | ||
} | ||
} |
101 changes: 101 additions & 0 deletions
101
...rc/main/java/com/asyncgate/signaling_server/security/filter/FilterChannelInterceptor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package com.asyncgate.signaling_server.security.filter; | ||
|
||
import com.asyncgate.signaling_server.security.constant.Constants; | ||
import com.asyncgate.signaling_server.security.utility.JsonWebTokenUtil; | ||
import io.jsonwebtoken.Claims; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.http.HttpStatus; | ||
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.stereotype.Component; | ||
import org.springframework.web.server.ResponseStatusException; | ||
|
||
@Component | ||
public class FilterChannelInterceptor implements ChannelInterceptor { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(FilterChannelInterceptor.class); | ||
private static final String AUTHORIZATION_HEADER = "Authorization"; | ||
private static final String BEARER_PREFIX = "Bearer "; | ||
private final JsonWebTokenUtil jsonWebTokenUtil; | ||
|
||
public FilterChannelInterceptor(JsonWebTokenUtil jsonWebTokenUtil) { | ||
this.jsonWebTokenUtil = jsonWebTokenUtil; | ||
} | ||
|
||
private String extractToken(String headerValue) { | ||
if (headerValue == null || headerValue.trim().isEmpty()) { | ||
return null; | ||
} | ||
String token = headerValue.trim(); | ||
return token.toLowerCase().startsWith(BEARER_PREFIX.toLowerCase()) ? token.substring(BEARER_PREFIX.length()) : token; | ||
} | ||
|
||
@Override | ||
public Message<?> preSend(Message<?> message, MessageChannel channel) { | ||
StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(message); | ||
log.info("📥 [STOMP] Command: {}, sessionId: {}", headerAccessor.getCommand(), headerAccessor.getSessionId()); | ||
|
||
if (StompCommand.CONNECT.equals(headerAccessor.getCommand())) { | ||
String rawAuth = headerAccessor.getFirstNativeHeader(AUTHORIZATION_HEADER); | ||
log.info("🔑 [STOMP] Raw Authorization Header: {}", rawAuth); | ||
String jwtToken = extractToken(rawAuth); | ||
if (jwtToken == null || jwtToken.isEmpty()) { | ||
log.error("🚨 [STOMP] Access Token is missing or improperly formatted!"); | ||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Access token is missing"); | ||
} | ||
|
||
if(headerAccessor.getSessionAttributes().get("userId") == null) { | ||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "userId is missing"); | ||
} | ||
log.info("✅ [STOMP] CONNECT 요청 처리 완료"); | ||
} | ||
return message; | ||
} | ||
|
||
@Override | ||
public void postSend(Message<?> message, MessageChannel channel, boolean sent) { | ||
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); | ||
log.info("📡 [STOMP] Command: {}, sessionId: {}, sent: {}", accessor.getCommand(), accessor.getSessionId(), sent); | ||
|
||
if (StompCommand.CONNECT.equals(accessor.getCommand())) { | ||
log.info("✅ [STOMP] CONNECT 성공 - sessionId: {}", accessor.getSessionId()); | ||
handleConnect(accessor); | ||
log.info("🔎 [STOMP] CONNECTED 프레임 헤더: {}", accessor.getMessageHeaders()); | ||
accessor.getMessageHeaders().forEach((key, value) -> log.info("messageHeader = {}: {}", key, value)); | ||
} else if (StompCommand.DISCONNECT.equals(accessor.getCommand())) { | ||
log.info("🔌 [STOMP] DISCONNECT 요청 - sessionId: {}", accessor.getSessionId()); | ||
handleDisconnect(accessor); | ||
} | ||
} | ||
|
||
private void handleDisconnect(StompHeaderAccessor accessor) { | ||
log.info("🔌 [STOMP] WebSocket 연결 해제 - sessionId: {}", accessor.getSessionId()); | ||
} | ||
|
||
private void handleConnect(StompHeaderAccessor accessor) { | ||
String currentSessionId = accessor.getSessionId(); | ||
if (currentSessionId == null) { | ||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Not session now"); | ||
} | ||
String rawAuth = accessor.getFirstNativeHeader(AUTHORIZATION_HEADER); | ||
String jwtToken = extractToken(rawAuth); | ||
if (jwtToken == null) { | ||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "JWT token is missing"); | ||
} | ||
|
||
Claims claims = jsonWebTokenUtil.validate(jwtToken); | ||
|
||
String memberId = claims.get(Constants.MEMBER_ID_CLAIM_NAME, String.class); | ||
|
||
// LoginSessionRequest loginSessionRequest = new LoginSessionRequest(LoginType.LOGIN, currentSessionId, currentUserId); | ||
// StateRequest stateRequest = new StateRequest(StatusType.CONNECT, currentUserId); | ||
// ToDo: 상태관리 서버에 로그인 전달 (주석 유지) | ||
// val guildIds = guildClient.getGuildIds(jwtToken) | ||
// 시그널링 서버에 전달 (주석 유지) | ||
// messageSender.signaling(stateTopic, stateRequest) | ||
} | ||
} |
Oops, something went wrong.