Bỏ qua nội dung

WebSocket / STOMP

Mục tiêu

Thiết lập kênh realtime để Backend có thể đẩy thông báo và sự kiện listing tới client đã đăng nhập mà không cần polling liên tục. Trang này là nguồn contract duy nhất cho STOMP-over-WebSocket giữa FE/Mobile và BE.

Tổng quan hệ thống

  • WebSocket là hạ tầng realtime dùng chung cho notification, listing live-update và các kênh realtime tương lai.
  • Giao thức chuẩn hiện tại: STOMP over WebSocket.
  • Endpoint native: /api/ws.
  • Endpoint fallback SockJS: /api/ws-sockjs.
  • Backend dùng simple broker in-memory với broker prefixes /topic, /queue.
  • User destination prefix là /user, dùng cho message riêng từng user.
  • Auth diễn ra ở STOMP CONNECT, không dựa vào HTTP handshake.

Phạm vi

Trong phạm vi:

  • STOMP-over-WebSocket endpoint native /api/ws.
  • SockJS fallback endpoint /api/ws-sockjs.
  • JWT access token trong STOMP native header Authorization: Bearer <token>.
  • Server push notification mới tới /user/queue/notifications.
  • Server push unread count tới /user/queue/notifications.unread-count.
  • Server push payment order status tới /user/queue/payment-orders.
  • Server broadcast listing event tới /topic/listings/{listingId}.
  • Authorization cho SEND, SUBSCRIBE, admin topic và user queue.
  • Rate limit inbound WebSocket frame.
  • Heartbeat STOMP client/server theo cấu hình runtime.

Ngoài phạm vi:

  • Chat realtime, presence, typing indicator hoặc collaborative editing.
  • Push notification ngoài app qua FCM/APNS/email/SMS.
  • External message broker như RabbitMQ/Redis STOMP relay ở giai đoạn hiện tại.
  • Thiết kế UI notification center, badge unread hoặc listing dashboard.
  • Offline queue trên client khi mất mạng.

User Stories

  • user đã đăng nhập, tôi muốn nhận notification realtime để biết ngay khi có sự kiện mới liên quan đến tài khoản.
  • client app, tôi muốn subscribe unread count realtime để cập nhật badge thông báo mà không polling liên tục.
  • user đang chờ thanh toán, tôi muốn nhận realtime payment status khi IPN/webhook xác nhận top-up thành công để màn chờ cập nhật ngay.
  • broker, tôi muốn nhận sự kiện listing realtime để dashboard có thể cập nhật danh sách/tình trạng tin nhanh hơn.
  • Backend service, tôi muốn publish message qua một facade chung để không phụ thuộc trực tiếp vào chi tiết broker.
  • hệ thống bảo mật, tôi muốn WebSocket CONNECT được xác thực bằng access token và các subscription nhạy cảm được kiểm soát.

Kết nối và xác thực

Client kết nối tới /api/ws hoặc /api/ws-sockjs, sau đó gửi STOMP CONNECT kèm native header:

Authorization: Bearer <accessToken>

Quy tắc:

  • Token phải là JWT access token hợp lệ.
  • Refresh token không được dùng cho WebSocket.
  • HTTP upgrade endpoint public ở tầng security, nhưng CONNECT thiếu/sai token bị reject.
  • Principal sau khi xác thực dùng userId làm name, để server push được qua /user/queue/**.
  • Allowed origins đọc từ APP_WS_ALLOWED_ORIGINS hoặc fallback CORS_ALLOWED_ORIGINS.

Destination contract

DestinationLoạiConsumerMục đích
/user/queue/notificationsuser queueFE/MobileNhận NotificationResponse mới của user hiện tại
/user/queue/notifications.unread-countuser queueFE/MobileNhận unread count mới nhất
/user/queue/payment-ordersuser queueFE/MobileNhận PaymentOrderRealtimePayload khi payment order của user đổi trạng thái
/user/queue/errorsuser queueFE/MobileNhận lỗi STOMP có cấu trúc
/topic/listings/{listingId}topicFE/MobileNhận listing event theo từng listing
/topic/admin/**topicAdminKênh realtime admin, yêu cầu role admin
/app/**application destinationFE/Mobile -> BEPrefix dành cho message client gửi lên BE khi có @MessageMapping

Authorization

  • Client đã xác thực được subscribe /user/queue/**/topic/**.
  • Subscribe /topic/admin hoặc /topic/admin/** yêu cầu role admin.
  • Client chỉ được SEND tới /app/**.
  • Client không được SEND trực tiếp vào /topic/**, /queue/** hoặc /user/**.
  • Inbound frame đi qua rate limit bucket websocket.

Acceptance Criteria

  • Backend cung cấp STOMP WebSocket endpoint /api/ws.
  • Backend cung cấp SockJS fallback endpoint /api/ws-sockjs.
  • HTTP upgrade cho /ws/**/ws-sockjs/** được public ở lớp HTTP security, nhưng STOMP CONNECT bắt buộc có JWT access token hợp lệ.
  • Client gửi token qua STOMP native header Authorization với format Bearer <accessToken>.
  • Token dùng cho WebSocket phải là access token, không chấp nhận refresh token hoặc token sai subject.
  • Khi JWT hợp lệ, WebSocket principal dùng userId làm name để server push tới đúng /user/queue/**.
  • Broker hỗ trợ destination /topic/**/queue/**, application destination prefix là /app.
  • Client chỉ được SEND tới destination dưới /app/**; client không được tự gửi vào /topic/** hoặc /queue/**.
  • Client đã xác thực được subscribe /topic/**/user/queue/**.
  • Subscription tới /topic/admin hoặc /topic/admin/** yêu cầu role admin.
  • Server push notification cá nhân tới /user/queue/notifications.
  • Server push unread count cá nhân tới /user/queue/notifications.unread-count.
  • Server push payment order status cá nhân tới /user/queue/payment-orders sau khi payment transaction commit.
  • Server broadcast listing event tới /topic/listings/{listingId}.
  • Khi có lỗi xử lý message, server trả lỗi có cấu trúc qua /user/queue/errors nếu lỗi đi qua STOMP handler.
  • WebSocket heartbeat client/server đọc từ cấu hình runtime.
  • Allowed origins cho WebSocket đọc từ cấu hình app.websocket.allowed-origins.
  • Inbound WebSocket frame đi qua rate limit để giảm rủi ro spam CONNECT/SEND/SUBSCRIBE.