Skip to content

Commit 5f3f3c9

Browse files
authored
feat(be) : WebSocket 연결 구현 (#17)
* feat : WebSocketConfig 작성 * feat: JwtHandshakeInterceptor 구현 JwtUtil은
1 parent 5909398 commit 5f3f3c9

File tree

3 files changed

+97
-0
lines changed

3 files changed

+97
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.back.koreaTravelGuide.common.config.websocket
2+
3+
import com.back.koreaTravelGuide.common.util.JwtUtil
4+
import org.springframework.http.HttpMethod
5+
import org.springframework.http.server.ServerHttpRequest
6+
import org.springframework.http.server.ServerHttpResponse
7+
import org.springframework.stereotype.Component
8+
import org.springframework.web.socket.WebSocketHandler
9+
import org.springframework.web.socket.server.HandshakeInterceptor
10+
import org.springframework.web.util.UriComponentsBuilder
11+
import java.lang.Exception
12+
import java.net.URI
13+
14+
@Component
15+
class JwtHandshakeInterceptor : HandshakeInterceptor {
16+
override fun beforeHandshake(
17+
request: ServerHttpRequest,
18+
response: ServerHttpResponse,
19+
wsHandler: WebSocketHandler,
20+
attributes: MutableMap<String, Any>,
21+
): Boolean {
22+
val uri: URI = request.uri
23+
val method = request.method
24+
25+
// ✅ SockJS 호환을 위한 임시 우회 (브라우저 테스트용)
26+
// - SockJS는 /info 요청을 토큰 없이 보냄
27+
// - 추후 순수 WebSocket 전환 시 제거 예정
28+
if (HttpMethod.OPTIONS == method || uri.path.endsWith("/info")) {
29+
return true
30+
}
31+
32+
val token =
33+
UriComponentsBuilder.fromUri(request.uri)
34+
.build()
35+
.queryParams
36+
.getFirst("token")
37+
?: return false // 토큰 없으면 연결 거부
38+
39+
return if (JwtUtil.validateToken(token)) {
40+
val userId = JwtUtil.getUserIdFromToken(token)
41+
attributes["userId"] = userId
42+
true
43+
} else {
44+
false
45+
}
46+
}
47+
48+
override fun afterHandshake(
49+
request: ServerHttpRequest,
50+
response: ServerHttpResponse,
51+
wsHandler: WebSocketHandler,
52+
exception: Exception?,
53+
) {
54+
}
55+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.back.koreaTravelGuide.common.config.websocket
2+
3+
import org.springframework.context.annotation.Configuration
4+
import org.springframework.messaging.simp.config.MessageBrokerRegistry
5+
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker
6+
import org.springframework.web.socket.config.annotation.StompEndpointRegistry
7+
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer
8+
9+
@Configuration
10+
@EnableWebSocketMessageBroker
11+
class WebSocketConfig(
12+
private val jwtHandshakeInterceptor: JwtHandshakeInterceptor,
13+
) : WebSocketMessageBrokerConfigurer {
14+
override fun registerStompEndpoints(registry: StompEndpointRegistry) {
15+
registry.addEndpoint("/ws/chat")
16+
.addInterceptors(JwtHandshakeInterceptor()) // JWT 체크
17+
.setAllowedOrigins("*")
18+
.withSockJS() // 개발 중 호환성
19+
}
20+
21+
override fun configureMessageBroker(registry: MessageBrokerRegistry) {
22+
registry.enableSimpleBroker("/sub") // 구독 경로
23+
registry.setApplicationDestinationPrefixes("/app") // 클라이언트에서 보낼 때
24+
}
25+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.back.koreaTravelGuide.common.util
2+
3+
/*
4+
임시 구현: 테스트 용
5+
토큰이 "test-token"이면 유효하다고 간주
6+
완성 후 이 파일은 삭제 또는 대체
7+
*/
8+
object JwtUtil {
9+
fun validateToken(token: String?): Boolean {
10+
// 테스트용: "test-token"이면 통과
11+
return token != null && token == "test-token"
12+
}
13+
14+
fun getUserIdFromToken(token: String): String {
15+
return "user123" // 테스트용 고정 사용자 ID
16+
}
17+
}

0 commit comments

Comments
 (0)