Spring boot websocket + RabbitMq - возникают проблемы с пользовательской настройкой заголовка для указанных конечных точек c - PullRequest
0 голосов
/ 14 января 2020

Я успешно настроил websocket в своем проекте, настроив его свойства безопасности, он работает нормально, как и ожидалось. Однако у меня есть несколько очень интересных случаев, когда в зависимости от типа пользователя я должен устанавливать перехватчики для соответствующих конечных точек, в которых я устанавливаю свои атрибуты заголовка, такие как (например) ID.

  1. Я не хочу запрещать неизвестным пользователям подключаться к некоторым конечным точкам. Что я имею в виду, если пользователь не авторизован, доступ к некоторым конечным точкам невозможен, но не ко всем.
  2. Я хочу проверить, существуют ли заголовки до рукопожатия. Я использовал EventListeners, но он настроен для всех конечных точек, но я хочу указать конкретные c конечные точки, такие как / user / queue / smth

Вот файл конфигурации websocket, который у меня есть создано до сих пор

 Configuration
@EnableWebSocketMessageBroker
@Order(Ordered.HIGHEST_PRECEDENCE + 99)
class WebSocketConfig @Autowired constructor(
        val jwtTokenUtil: TokenProvider
) : WebSocketMessageBrokerConfigurer {

    @Autowired
    @Resource(name = "userService")
    private val userDetailsService: UserDetailsService? = null

    @Autowired
    private lateinit var authenticationManager: AuthenticationManager

    @Value("\${spring.rabbitmq.username}")
    private val userName: String? = null
    @Value("\${spring.rabbitmq.password}")
    private val password: String? = null
    @Value("\${spring.rabbitmq.host}")
    private val host: String? = null
    @Value("\${spring.rabbitmq.port}")
    private val port: Int = 0
    @Value("\${endpoint}")
    private val endpoint: String? = null
    @Value("\${destination.prefix}")
    private val destinationPrefix: String? = null
    @Value("\${stomp.broker.relay}")
    private val stompBrokerRelay: String? = null

    override fun configureMessageBroker(config: MessageBrokerRegistry) {
        config.enableStompBrokerRelay("/queue/", "/topic/")
                .setRelayHost(host!!)
                .setRelayPort(port)
                .setSystemLogin(userName!!)
                .setSystemPasscode(password!!)
        config.setApplicationDestinationPrefixes(destinationPrefix!!)
    }

    override fun registerStompEndpoints(registry: StompEndpointRegistry) {
        registry.addEndpoint("/ws").addInterceptors(customHttpSessionHandshakeInterceptor()).setAllowedOrigins("*").withSockJS()
    }

    @Bean
    fun customHttpSessionHandshakeInterceptor(): CustomHttpSessionHandshakeInterceptor {
        return CustomHttpSessionHandshakeInterceptor()
    }




    override fun configureClientInboundChannel(registration: ChannelRegistration) {
        registration.interceptors(object : ChannelInterceptor {
            override fun preSend(message: Message<*>, channel: MessageChannel): Message<*> {
                val accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor::class.java)
                if (StompCommand.CONNECT == accessor!!.command) {
                    val authorization = accessor.getNativeHeader("X-Authorization")
                    println("X-Authorization: {$authorization}")
                    val authToken = authorization!![0].split(" ")[1]

                    val username = jwtTokenUtil.getUsernameFromToken(authToken)

                    if (username != null) {
                        if(username.contains("@")) {
                            val userDetails = userDetailsService!!.loadUserByUsername(username)

                            if (jwtTokenUtil.validateToken(authToken, userDetails)) {
                                val authentication = jwtTokenUtil.getAuthentication(authToken, SecurityContextHolder.getContext().authentication, userDetails)
                                accessor.user = authentication
                            }
                        } else {
                            val authorities = jwtTokenUtil.getAuthoritiesFromToken(authToken)
                            val usernamePasswordAuthenticationToken = UsernamePasswordAuthenticationToken(username, "", authorities)
                            val authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken)
                            accessor.user = authentication
                        }
                    }


                }
                return message
            }
        })
    }
}

Вот прослушиватели событий, которые я использую для установки идентификатора в заголовок

    @Component
class WebSocketEvents  {


    @EventListener
    fun handleSessionConnected(event: SessionConnectEvent) {
        val headers = SimpMessageHeaderAccessor.wrap(event.message)
        if ( headers.getNativeHeader("chat_id") != null && headers.getNativeHeader("chat_id")!!.isNotEmpty()){
            val chatId = headers.getNativeHeader("chat_id")!![0]
            if (headers.sessionAttributes != null)
                headers.sessionAttributes!!["chat_id"] = chatId
        }
        //else //TODO THROW AN EXCEPTION OR DO SMTH

//        val joiningUser = ChatRoomUser(event.user!!.name)
//
//        chatRoomService!!.join(joiningUser, chatRoomService!!.findById(chatRoomId))
    }

    @EventListener
    fun handleSessionDisconnect(event: SessionDisconnectEvent) {
        val headers = SimpMessageHeaderAccessor.wrap(event.message)
        val chatRoomId = headers.sessionAttributes!!["chat_id"].toString()
//        val leavingUser = ChatRoomUser(event.user!!.name)
//
//        chatRoomService!!.leave(leavingUser, chatRoomService!!.findById(chatRoomId))
    }
}

Настройка безопасности простой

@Configuration
class WebSocketSecurityConfigurer : AbstractSecurityWebSocketMessageBrokerConfigurer() {

    override fun configureInbound(messages: MessageSecurityMetadataSourceRegistry) {
        messages.anyMessage().authenticated()
    }

    override fun sameOriginDisabled(): Boolean {
        return true
    }

}

Перехватчик, который у меня до сих пор. Я знаю, что использовал перехватчики и прослушиватели событий для установки заголовков, но, насколько я понимаю, с помощью обработчиков событий вы можете устанавливать заголовки для всех соединений, верно? но я не хочу этого Я хочу установить для конкретных c конечных точек. однако я не уверен, что перехватчики - единственный способ добиться этого.

 class CustomHttpSessionHandshakeInterceptor : HandshakeInterceptor {

    @Throws(Exception::class)
    override fun beforeHandshake(request: ServerHttpRequest, response: ServerHttpResponse, wsHandler: WebSocketHandler, attributes: MutableMap<String, Any>): Boolean {
        if (request is ServletServerHttpRequest) {
            attributes["custom-header"] = "foo"
        }
        return true
    }

    override fun afterHandshake(request: ServerHttpRequest, response: ServerHttpResponse, wsHandler: WebSocketHandler, @Nullable exception: java.lang.Exception?) {
    }
}

Давайте подведем итоги, что необходимо сделать здесь. Первые пользователи с авторизацией могут получить доступ к указанным c конечным точкам, предоставив токены в работающих заголовках, но неизвестные пользователи также могут получить доступ к некоторым конечным точкам, к которым им разрешено, не предоставляя токены в заголовках -> здесь это должно быть проверено с использованием перехватчиков ? Во-вторых, мне нужно проверить, чтобы некоторые дополнительные заголовки были установлены пользователями, чтобы я снова проверил их, используя перехватчики, и установил эти заголовки в собственные заголовки. Есть ли возможный и правильный способ добиться этого. Заранее спасибо!

...