Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[기능 요청] ServerClient의 getServerList에서 parameter로 token이 아니라 principal이나 userId를 받았으면 좋겠습니다 #260

Open
minisundev opened this issue Oct 9, 2024 · 5 comments
Assignees
Labels
Auth 인증기능 Request 구현 요청

Comments

@minisundev
Copy link
Member

minisundev commented Oct 9, 2024

🤔 어떤 기능인가요?

원래 다음과 같이 Controller에서 header의 token을 통해 user의 정보를 authClient에 요청을 보내서 추출해 사용하고 있었습니다.

@MessageMapping("/chat/create")
  fun createChat(
    @Payload @Validated request: CreateChatRequest,
    **headerAccessor: SimpMessageHeaderAccessor, //-> 이 부분**
  ) {
    val token = headerAccessor.getFirstNativeHeader("Authorization") ?: throw GlobalException(ErrorCode.MISSING_TOKEN)
    **val userId = authClient.getTokenInfo(token).data!!.userId //->이 부분**
    val contextId = request.contextId

    val result =
      when (request.type) {
        ChatType.ROOM -> chatService.createRoomChat(request, userId)
        ChatType.SERVER ->
          chatService.createServerChat(
            request,
            userId,
            serverClient.getServerList(token, GetServerCondition()).body!!.data!!,
          )
      }
    simpMessagingTemplate.convertAndSend("/topic/chatroom/$contextId", result)
  }

그러나 인증을 WebSocket 연결 시에 Spring Security의 Principal에 user 정보를 담아서 한 번만 요청하려고 다음과 같이 Interceptor로 따로 뽑았습니다

@Configuration
@EnableWebSocketMessageBroker
class WebSocketConfig(
  val authClient: AuthClient,
) : WebSocketMessageBrokerConfigurer {
  @Value("\${url.front}")
  val frontUrl: String = ":63343"
  
  @Bean
  fun webSocketAuthInterceptor(): ChannelInterceptor {
    return object : ChannelInterceptor {
      override fun preSend(
        message: Message<*>,
        channel: MessageChannel,
      ): Message<*>? {
        val simpMessageType = SimpMessageHeaderAccessor.getMessageType(message.headers)
        if (simpMessageType == SimpMessageType.CONNECT) {
          val token = SimpMessageHeaderAccessor.wrap(message).getFirstNativeHeader("Authorization")
          if (token != null) {
            val userId = authClient.getTokenInfo(token).data!!.userId
            val principal = UsernamePasswordAuthenticationToken(userId, null, emptyList())
            SimpMessageHeaderAccessor.getAccessor(message, SimpMessageHeaderAccessor::class.java)!!.user = principal
          } else {
            throw GlobalException(ErrorCode.MISSING_TOKEN)
          }
        }
        return message
      }
    }
}

그런 후에 Principal만 받아서 동작할 수 있도록 했습니다

@MessageMapping("/chat/create")
  fun createChat(
    @Payload @Validated request: CreateChatRequest,
    **principal: Principal, //-> 이 부분**
    headerAccessor: SimpMessageHeaderAccessor,
  ) {
    val token = headerAccessor.getFirstNativeHeader("Authorization") ?: throw GlobalException(ErrorCode.MISSING_TOKEN)
    **val userId = principal.name //-> 이 부분**
    val contextId = request.contextId

    val result =
      when (request.type) {
        ChatType.ROOM -> chatService.createRoomChat(request, userId)
        ChatType.SERVER ->
          chatService.createServerChat(
            request,
            userId,
            serverClient.getServerList(token, GetServerCondition()).body!!.data!!,
          )
      }
    simpMessagingTemplate.convertAndSend("/topic/chatroom/$contextId", result)
  }

🚩 문제점

이때 getServerList도 Interceptor로 넣으려고 했습니다.
그러나 웹소켓 연결 이후에 해당 서버가 삭제될 수도 있고 탈퇴당할 수도 있는데 serverList를 웹소켓 연결 시에만 넣는 것이 말이 되지 않습니다.

😇논의사항

그래서 token을 보내자니 불필요하게 controller에서 token을 인자로 계속 받아야 합니다
principal이나 username / userId로만 조회할 수 있는 기능이 있었으면 좋겠습니다
그냥 서버가 삭제되든말든 웹소켓 재연결이 될 때까지 서버 리스트를 유지할까요?
serverClient가 controller끼리의 요청이라서 token을 받는것이 API 설계상으로 더 맞는걸까요?
그냥 token을 넘길까요? 다른 분들은 어떻게 생각하시나요?

@minisundev minisundev added Auth 인증기능 Request 구현 요청 labels Oct 9, 2024
@minisundev
Copy link
Member Author

웹소켓 세션에다가 서버 리스트를 저장하면 보안에도 안 좋고 하니까 그냥 토큰으로 받아서 날릴까요

@yudonggeun
Copy link
Contributor

웹 소켓 세션에 서버 리스트를 저장하면 보안에 안 좋은 이유가 뭔가요?

controller에서 token을 인자로 받는다는 것이 웹소켓 컨트롤러인가요? API 컨트롤러인가요?

서비 리스트는 세션에 저장하는 것은 나쁘지 않다고 생각해요. 만약 서버가 삭제된다면 그 이벤트를 접속한 유저에게 전달을 하기 때문에 세션 정보도 그 시점에서 같이 갱신하면 정합성 문제는 없을 것 같습니다. 다만 서버 리스트 정보를 캐싱하는 비용과 필요할 때마다 조회하는 비용 중에서 어떤 것이 저렴한지를 고민하고 선택하면 좋을 것 같습니다.

serverClient가 controller끼리의 요청이라서 token을 받는것이 API 설계상으로 더 맞는걸까요?
이 부분은 좀 더 설명을 해줄 수 있을까요?

@minisundev
Copy link
Member Author

웹 소켓 세션에 서버 리스트를 저장하면 보안에 안 좋은 이유가 뭔가요?

controller에서 token을 인자로 받는다는 것이 웹소켓 컨트롤러인가요? API 컨트롤러인가요?

서비 리스트는 세션에 저장하는 것은 나쁘지 않다고 생각해요. 만약 서버가 삭제된다면 그 이벤트를 접속한 유저에게 전달을 하기 때문에 세션 정보도 그 시점에서 같이 갱신하면 정합성 문제는 없을 것 같습니다. 다만 서버 리스트 정보를 캐싱하는 비용과 필요할 때마다 조회하는 비용 중에서 어떤 것이 저렴한지를 고민하고 선택하면 좋을 것 같습니다.

serverClient가 controller끼리의 요청이라서 token을 받는것이 API 설계상으로 더 맞는걸까요?
이 부분은 좀 더 설명을 해줄 수 있을까요?

serverClient가 controller끼리의 요청이라서 token을 받는것이 API 설계상으로 더 맞는걸까요? 이 부분은 좀 더 설명을 해줄 수 있을까요?
=> serverClient가 serverController에 요청하는건데 serverController의 parameter에는 token이 들어가는 것이 좀 더 맞는 것 같아서요

세션에다 서버 리스트를 저장하면 세션 하이재킹같은 걸 당할 수도 있다고 생각했습니다
그렇지만 다시 생각해보면 사용자가 속한 서버 정보가 유출되어도 그렇게 문제는 없을 것 같네요

서버가 삭제될때 세션 정보를 갱신하면 정합성 문제가 없을 것 같네요!
서버 연결도 웹소켓으로 하면 Interceptor에서 서버가 삭제될 때마다 서버 리스트를 새로 세션에 저장하는 것을 처리할 수 있을 것 같지만 Http 연결이라서 따로 캐싱해놨다가 조회해야 할 것 같네요
그래도 Interceptor에서 서버 정보가 필요한 MESSAGE 타입 요청이 들어왔을때마다 서버 리스트 갱신하는 식으로는 할 수 있을 것 같아요 어차피 Controller에서 serverClient로 부르나 Interceptor에서 부르나 동일한 횟수인 것 같아서요

@yudonggeun
Copy link
Contributor

세션 하이재킹으로 인해서 세션 정보가 털릴 것을 염려해서 세션을 활용하지 않는 것보다는 세션 하이재킹을 방지하기 위한 보장 솔루션을 적용하는 것은 어떨까요?

서버 목록을 세션에 저장하는 것이 아니라 클라이언트의 로컬 스토리지에 저장하는 것이 좋다고 생각합니다. 일단 민감한 정보도 아니고 서버 목록을 세션에 저장하기 위한 리소스 비용이 아까운 것 같아요.

@minisundev
Copy link
Member Author

minisundev commented Oct 23, 2024

세션에 저장하면 서버 리소스가 낭비될 것 같기는 하네요!
토큰으로 serverClient에다 소속된 서버 리스트를 요청하지 않고 userId 로 요청하게 되면 웹소켓 세션에다가 user 정보만 유지하면 되고 코드도 간결해질 것 같은데 어떻게 생각하시나요

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Auth 인증기능 Request 구현 요청
Projects
Status: No status
Development

No branches or pull requests

2 participants