feat(user-creator-chat): WebSocket 인증 핸드셰이크를 추가한다

This commit is contained in:
2026-06-18 16:08:14 +09:00
parent 5cab3558c0
commit a170c82a92
5 changed files with 265 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
package kr.co.vividnext.sodalive.v2.usercreatorchat.websocket
import kr.co.vividnext.sodalive.jwt.JwtFilter
import kr.co.vividnext.sodalive.jwt.TokenProvider
import kr.co.vividnext.sodalive.member.MemberAdapter
import org.springframework.http.server.ServerHttpRequest
import org.springframework.http.server.ServerHttpResponse
import org.springframework.stereotype.Component
import org.springframework.util.StringUtils
import org.springframework.web.socket.WebSocketHandler
import org.springframework.web.socket.server.HandshakeInterceptor
@Component
class UserCreatorChatWebSocketAuthInterceptor(
private val tokenProvider: TokenProvider
) : HandshakeInterceptor {
override fun beforeHandshake(
request: ServerHttpRequest,
response: ServerHttpResponse,
wsHandler: WebSocketHandler,
attributes: MutableMap<String, Any>
): Boolean {
val token = resolveToken(request) ?: return false
if (!tokenProvider.validateToken(token)) {
return false
}
val authentication = try {
tokenProvider.getAuthentication(token)
} catch (e: RuntimeException) {
return false
}
val principal = authentication.principal as? MemberAdapter ?: return false
val memberId = principal.member.id ?: return false
attributes[MEMBER_ID_ATTRIBUTE] = memberId
attributes[AUTHENTICATION_ATTRIBUTE] = authentication
return true
}
override fun afterHandshake(
request: ServerHttpRequest,
response: ServerHttpResponse,
wsHandler: WebSocketHandler,
exception: Exception?
) {
}
private fun resolveToken(request: ServerHttpRequest): String? {
val bearerToken = request.headers.getFirst(JwtFilter.AUTHORIZATION_HEADER)
if (StringUtils.hasText(bearerToken) && bearerToken!!.startsWith(BEARER_PREFIX)) {
return bearerToken.substring(BEARER_PREFIX.length)
}
return null
}
companion object {
const val MEMBER_ID_ATTRIBUTE = "memberId"
const val AUTHENTICATION_ATTRIBUTE = "authentication"
private const val BEARER_PREFIX = "Bearer "
}
}

View File

@@ -0,0 +1,29 @@
package kr.co.vividnext.sodalive.v2.usercreatorchat.websocket
import org.springframework.context.annotation.Configuration
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
class UserCreatorChatWebSocketConfig(
private val handler: UserCreatorChatWebSocketHandler,
private val authInterceptor: UserCreatorChatWebSocketAuthInterceptor
) : WebSocketConfigurer {
override fun registerWebSocketHandlers(registry: WebSocketHandlerRegistry) {
registry.addHandler(handler, ENDPOINT)
.addInterceptors(authInterceptor)
.setAllowedOrigins(
"http://localhost:8888",
"https://creator.sodalive.net",
"https://test-creator.sodalive.net",
"https://test-admin.sodalive.net",
"https://admin.sodalive.net"
)
}
companion object {
const val ENDPOINT = "/ws/v2/user-creator-chat"
}
}

View File

@@ -0,0 +1,7 @@
package kr.co.vividnext.sodalive.v2.usercreatorchat.websocket
import org.springframework.stereotype.Component
import org.springframework.web.socket.handler.TextWebSocketHandler
@Component
class UserCreatorChatWebSocketHandler : TextWebSocketHandler()