Skip to content
This repository has been archived by the owner on May 19, 2024. It is now read-only.

Commit

Permalink
[WEAV-327] 채팅 유저 인증 개발 (#235)
Browse files Browse the repository at this point in the history
  • Loading branch information
waterfogSW authored Apr 11, 2024
1 parent 299a752 commit 79ac873
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ fun interface SendChatMessage {

fun invoke(
roomId: UUID,
senderId: UUID,
userId: UUID,
contents: List<ChatMessage.Content>,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ class SendChatMessageService(

override fun invoke(
roomId: UUID,
senderId: UUID,
userId: UUID,
contents: List<ChatMessage.Content>,
) {
ChatMessage.createByUser(
roomId = roomId,
userId = senderId,
userId = userId,
contents = contents,
).also {
chatMessagePublisher.publish(it)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@ data class UserAuthentication(
}
}

override fun getName(): String {
return userId.toString()
}

}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.studentcenter.weave.bootstrap.chat.controller

import com.studentcenter.weave.application.chat.port.inbound.SendChatMessage
import com.studentcenter.weave.application.user.vo.UserAuthentication
import com.studentcenter.weave.bootstrap.chat.dto.SendChatMessageRequest
import com.studentcenter.weave.support.common.uuid.UuidCreator
import org.springframework.messaging.handler.annotation.DestinationVariable
import org.springframework.messaging.handler.annotation.MessageMapping
import org.springframework.messaging.handler.annotation.SendTo
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.RequestBody
import java.util.*

@Controller
Expand All @@ -15,14 +15,14 @@ class ChatWebSocketController(
) {

@MessageMapping("/chat-rooms/{roomId}")
@SendTo("/topic/chat-rooms/{roomId}")
fun sendTextMessage(
fun sendChatMessage(
@DestinationVariable roomId: UUID,
request: SendChatMessageRequest,
@RequestBody request: SendChatMessageRequest,
userAuthentication: UserAuthentication,
) {
sendChatMessage.invoke(
roomId = roomId,
senderId = UuidCreator.create(), // TODO: 토큰 인증 구현
userId = userAuthentication.userId,
contents = request.contents,
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package com.studentcenter.weave.bootstrap.chat.config
package com.studentcenter.weave.bootstrap.common.config

import com.studentcenter.weave.bootstrap.common.security.interceptor.StompExceptionHandler
import com.studentcenter.weave.bootstrap.common.security.interceptor.StompAuthInterceptor
import org.springframework.context.annotation.Configuration
import org.springframework.messaging.simp.config.ChannelRegistration
import org.springframework.messaging.simp.config.MessageBrokerRegistry
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker
import org.springframework.web.socket.config.annotation.StompEndpointRegistry
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer

@Configuration
@EnableWebSocketMessageBroker
class WebSocketConfig : WebSocketMessageBrokerConfigurer {
class WebSocketConfig(
private val stompAuthInterceptor: StompAuthInterceptor,
private val stompExceptionHandler: StompExceptionHandler,
) : WebSocketMessageBrokerConfigurer {

override fun configureMessageBroker(registry: MessageBrokerRegistry) {
registry.enableSimpleBroker("/topic")
Expand All @@ -17,9 +23,14 @@ class WebSocketConfig : WebSocketMessageBrokerConfigurer {

override fun registerStompEndpoints(registry: StompEndpointRegistry) {
registry
.setErrorHandler(stompExceptionHandler)
.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
.withSockJS()
}

override fun configureClientInboundChannel(registration: ChannelRegistration) {
registration.interceptors(stompAuthInterceptor)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.studentcenter.weave.bootstrap.common.security.interceptor

import com.studentcenter.weave.application.user.service.util.UserTokenService
import com.studentcenter.weave.application.user.vo.UserAuthentication
import org.springframework.messaging.Message
import org.springframework.messaging.MessageChannel
import org.springframework.messaging.simp.SimpMessageHeaderAccessor
import org.springframework.messaging.simp.stomp.StompCommand
import org.springframework.messaging.simp.stomp.StompHeaderAccessor
import org.springframework.messaging.support.ChannelInterceptor
import org.springframework.messaging.support.MessageHeaderAccessor
import org.springframework.stereotype.Component

@Component
class StompAuthInterceptor(
private val userTokenService: UserTokenService,
) : ChannelInterceptor {

override fun preSend(
message: Message<*>,
channel: MessageChannel,
): Message<*>? {
val accessor = StompHeaderAccessor.wrap(message)
val command = accessor.command

when (command) {
StompCommand.CONNECT -> handleConnect(message)
else -> return message
}

return message
}

private fun handleConnect(message: Message<*>) {
val accessor = MessageHeaderAccessor
.getAccessor(message, SimpMessageHeaderAccessor::class.java)
?: throw IllegalStateException("Cannot get accessor")

extractToken(message)?.let { token ->
val userAuthentication = userTokenService
.resolveAccessToken(token)
.let { UserAuthentication.from(it) }
accessor.user = userAuthentication
}
}


private fun extractToken(message: Message<*>): String? {
val accessor = StompHeaderAccessor.wrap(message)
val bearerToken: String? = accessor.getFirstNativeHeader(AUTHORIZATION_HEADER)
return if (bearerToken != null && bearerToken.startsWith(BEARER_PREFIX)) {
bearerToken.substring(BEARER_PREFIX.length)
} else null
}

companion object {

private const val AUTHORIZATION_HEADER = "Authorization"
private const val BEARER_PREFIX = "Bearer "
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.studentcenter.weave.bootstrap.common.security.interceptor

import com.fasterxml.jackson.databind.ObjectMapper
import com.studentcenter.weave.bootstrap.common.exception.ErrorResponse
import com.studentcenter.weave.support.common.exception.CustomException
import com.studentcenter.weave.support.common.exception.SystemExceptionType
import org.springframework.messaging.Message
import org.springframework.messaging.simp.stomp.StompCommand
import org.springframework.messaging.simp.stomp.StompHeaderAccessor
import org.springframework.messaging.support.MessageBuilder
import org.springframework.stereotype.Component
import org.springframework.web.socket.messaging.StompSubProtocolErrorHandler

@Component
class StompExceptionHandler(
private val objectMapper: ObjectMapper,
) : StompSubProtocolErrorHandler() {

override fun handleClientMessageProcessingError(
clientMessage: Message<ByteArray>?,
ex: Throwable,
): Message<ByteArray>? {
return when (val cause: Throwable? = ex.cause) {
is CustomException -> handleCustomException(cause)
else -> handleException(ex)
}
}

private fun handleCustomException(customException: CustomException): Message<ByteArray> {
val response = ErrorResponse(
customException.type.code,
customException.message
)
val accessor = StompHeaderAccessor.create(StompCommand.ERROR)
accessor.setLeaveMutable(true)
return MessageBuilder.createMessage(
objectMapper.writeValueAsBytes(response),
accessor.messageHeaders
)
}

private fun handleException(ex: Throwable): Message<ByteArray> {
val response = ErrorResponse(
SystemExceptionType.INTERNAL_SERVER_ERROR.code,
ex.message.toString(),
)
val accessor = StompHeaderAccessor.create(StompCommand.ERROR)
accessor.setLeaveMutable(true)
return MessageBuilder.createMessage(
objectMapper.writeValueAsBytes(response),
accessor.messageHeaders
)
}

}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
package com.studentcenter.weave.support.security.authority

interface Authentication
import java.security.Principal

interface Authentication : Principal

0 comments on commit 79ac873

Please sign in to comment.