Мы успешно разработали webosocket + stomp + rabbitmq для нашего проекта с безопасностью. Он работает нормально, хотя у нас есть некоторые проблемы в решении следующего случая: Рабочий процесс этого веб-сокета работает следующим образом:
Мой вопрос: как я могу проверять пользователей, когда они попытаться подписаться на эту конечную точку? Я имею в виду, есть ли способ обработать каждую конкретную c подписку
Это наш код переднего плана. если вам нужна полная страница, я загрузлю ее
function connect() {
socket = new SockJS('http://localhost:9600/wsss/messages');
stompClient = Stomp.over(socket);
// var stompClient = Stomp.client("ws://localhost:9600/ws/messages");
// stompClient.connect({ 'chat_id' : chatRoomId,
// 'X-Authorization' : 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI0Iiwic2NvcGVzIjoiUk9MRV9BRE1JTiIsImVtYWlsIjoiYWRtaW5AZ21haWwuY29tIiwiaWF0IjoxNTc5MDgxMzg5LCJleHAiOjE1ODE2NzMzODl9.H3mnti0ZNtH6uLe-sOfrr5jzwssvGNcBiHGg-nUQ6xY' },
// stompSuccess, stompFailure);
stompClient.connect({ 'chatRoomId' : chatRoomId,
'login' : 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIyODg5Iiwic2NvcGVzIjoiUk9MRV9VU0VSLFJPTEVfTU9ERVJBVE9SIiwiaWF0IjoxNTgyMDMxMDA0LCJleHAiOjE1ODQ2MjMwMDR9.NGAAed4R46FgrtgyDmrLSrmd-o3tkqbF60vOg8vAWYg' },
stompSuccess, stompFailure);
}
function stompSuccess(frame) {
enableInputMessage();
successMessage("Your WebSocket connection was successfuly established!");
console.log(frame);
stompClient.subscribe('/user/queue/' + chatRoomId + '.messages', incomingMessages);
stompClient.subscribe('/topic/notification', incomingNotificationMessage);
// stompClient.subscribe('/app/join/notification', incomingNotificationMessage);
}
А вот код, который я использую для моего бэкэнда
@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("/websocket").setAllowedOrigins("*").setAllowedOrigins("*")
registry.addEndpoint("/websocket/messages").addInterceptors(customHttpSessionHandshakeInterceptor()).setAllowedOrigins("*")
registry.addEndpoint("/wsss/messages").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 || StompCommand.STOMP == accessor.command) {
val authorization = accessor.getNativeHeader("login")
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("chatRoomId") != null && headers.getNativeHeader("chatRoomId")!!.isNotEmpty()){
val chatId = headers.getNativeHeader("chatRoomId")!![0]
if (headers.sessionAttributes != null)
headers.sessionAttributes!!["chatRoomId"] = chatId
}
}
@EventListener
fun handleSessionDisconnect(event: SessionDisconnectEvent) {
val headers = SimpMessageHeaderAccessor.wrap(event.message)
val chatRoomId = headers.sessionAttributes!!["chatRoomId"].toString()
}
}
Пока что я попробовал: Как вы можете видеть выше, когда пользователь впервые подключается к конечной точке websocket http://localhost: 9600 / wsss / messages он отправляет токен и идентификатор чата (заголовки), и я обрабатываю это в компоненте прослушивателя событий, сбрасывая chatroomid в атрибуты заголовка. Что мне действительно нужно сделать, так это взять идентификатор чата, пока пользователь подписывается на это конкретное описание, и применить проверку, принадлежит ли он этому чату, и если да, просто дать ему разрешение | пусть он присоединится к чату, если не вернет ошибку. Я действительно ценю любые мысли или обходные пути!