令牌認證
Spring Security OAuth 提供令牌安全支援,包括 JSON Web Token (JWT)。您可以在 Web 應用程式(包括 WebSocket 上的 STOMP 互動)中使用它作為認證機制,如上一節所述(即透過基於 cookie 的會話維護身份)。
同時,基於 cookie 的會話並不總是最佳選擇(例如,在不維護伺服器端會話的應用程式中,或在通常使用請求頭進行認證的移動應用程式中)。
WebSocket 協議,RFC 6455“沒有規定伺服器在 WebSocket 握手期間可以認證客戶端的任何特定方式。”然而,在實踐中,瀏覽器客戶端只能使用標準的認證頭(即基本 HTTP 認證)或 cookie,而不能(例如)提供自定義頭。同樣,SockJS JavaScript 客戶端不提供透過 SockJS 傳輸請求傳送 HTTP 頭的方式。參見sockjs-client issue 196。相反,它允許傳送可用於傳送令牌的查詢引數,但這有其自身的缺點(例如,令牌可能會意外地與 URL 一起記錄在伺服器日誌中)。
上述限制適用於基於瀏覽器的客戶端,不適用於基於 Spring Java 的 STOMP 客戶端,後者支援透過 WebSocket 和 SockJS 請求傳送頭。 |
因此,希望避免使用 cookie 的應用程式可能在 HTTP 協議級別沒有好的認證替代方案。他們可能更傾向於在 STOMP 訊息協議級別使用請求頭進行認證。這樣做需要兩個簡單的步驟:
-
使用 STOMP 客戶端在連線時傳遞認證頭。
-
使用
ChannelInterceptor
處理認證頭。
下面的示例使用伺服器端配置註冊自定義認證攔截器。請注意,攔截器只需要認證並在 CONNECT Message
上設定使用者頭。Spring 會記錄並儲存已認證的使用者,並將其與同一會話上的後續 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)
進行標記來實現這一點。