Token 驗證

Spring Security OAuth 提供基於 Token 安全性的支援,包含 JSON Web Token (JWT)。您可以在 Web 應用程式中使用它作為驗證機制,包含 WebSocket 互動上的 STOMP,如前一節所述(也就是,透過基於 Cookie 的 Session 維護身分)。

同時,基於 Cookie 的 Session 並非總是最佳選擇(例如,在不維護伺服器端 Session 的應用程式中,或是在行動應用程式中,通常使用 Header 進行驗證)。

WebSocket 協定 RFC 6455 「未規定伺服器可以在 WebSocket 握手期間驗證用戶端的任何特定方式」。然而,實際上,瀏覽器用戶端只能使用標準驗證 Header(也就是基本 HTTP 驗證)或 Cookie,而不能(例如)提供自訂 Header。同樣地,SockJS JavaScript 用戶端不提供透過 SockJS 傳輸請求傳送 HTTP Header 的方式。請參閱 sockjs-client issue 196。相反地,它允許傳送查詢參數,您可以使用這些參數傳送 Token,但這有其自身的缺點(例如,Token 可能會不小心與伺服器記錄中的 URL 一起記錄)。

上述限制適用於基於瀏覽器的用戶端,但不適用於基於 Spring Java 的 STOMP 用戶端,後者確實支援透過 WebSocket 和 SockJS 請求傳送 Header。

因此,希望避免使用 Cookie 的應用程式可能在 HTTP 協定層級沒有任何好的驗證替代方案。他們可能更喜歡在 STOMP 訊息協定層級使用 Header 進行驗證,而不是使用 Cookie。這樣做需要兩個簡單步驟

  1. 在連線時使用 STOMP 用戶端傳遞驗證 Header。

  2. 使用 ChannelInterceptor 處理驗證 Header。

下一個範例使用伺服器端組態來註冊自訂驗證攔截器。請注意,攔截器只需要驗證並在 CONNECT Message 上設定使用者 Header。Spring 會記錄並儲存已驗證的使用者,並將其與同一 Session 上的後續 STOMP 訊息關聯。以下範例說明如何註冊自訂驗證攔截器

  • Java

  • Kotlin

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {

	@Override
	public void configureClientInboundChannel(ChannelRegistration registration) {
		registration.interceptors(new ChannelInterceptor() {
			@Override
			public Message<?> preSend(Message<?> message, MessageChannel channel) {
				StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
				if (StompCommand.CONNECT.equals(accessor.getCommand())) {
					// Access authentication header(s) and invoke accessor.setUser(user)
				}
				return message;
			}
		});
	}
}
@Configuration
@EnableWebSocketMessageBroker
class WebSocketConfiguration : WebSocketMessageBrokerConfigurer {

	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) {
					// Access authentication header(s) and invoke accessor.setUser(user)
				}
				return message
			}
		})
	}
}

另請注意,當您將 Spring Security 的授權用於訊息時,目前,您需要確保驗證 ChannelInterceptor 組態的順序在 Spring Security 之前。最好透過在 WebSocketMessageBrokerConfigurer 的自身實作中宣告自訂攔截器來完成,該實作標記為 @Order(Ordered.HIGHEST_PRECEDENCE + 99)