トークン認証

Spring Security OAuth [GitHub] (英語) は、JSON Web トークン(JWT)などのトークンベースのセキュリティのサポートを提供します。これは、前のセクションで説明したように、WebSocket を介した STOMP 対話を含む Web アプリケーションの認証メカニズムとして使用できます(つまり、Cookie ベースのセッションを通じて ID を維持します)。

同時に、Cookie ベースのセッションが常に最適とは限りません(たとえば、サーバー側セッションを維持しないアプリケーションや、認証にヘッダーを使用することが一般的なモバイルアプリケーション)。

WebSocket プロトコル、RFC 6455 [IETF] (英語) は、「WebSocket ハンドシェイク中にサーバーがクライアントを認証できる特定の方法を規定していません。」ただし、実際には、ブラウザークライアントは標準認証ヘッダー(つまり、基本的な HTTP 認証)または Cookie のみを使用でき、カスタムヘッダーを提供することはできません(たとえば)。同様に、SockJS JavaScript クライアントは、SockJS トランスポートリクエストで HTTP ヘッダーを送信する方法を提供しません。sockjs-client issue 196 [GitHub] (英語) を参照してください。代わりに、トークンを送信するために使用できるクエリパラメーターを送信できますが、独自の欠点があります(たとえば、トークンが誤ってサーバーログの URL に記録される場合があります)。

前述の制限はブラウザーベースのクライアントに対するものであり、Spring Java ベースの STOMP クライアントには適用されません。Spring Java ベースの STOMP クライアントは、WebSocket リクエストと SockJS リクエストの両方を含むヘッダーの送信をサポートします。

Cookie の使用を回避したいアプリケーションには、HTTP プロトコルレベルでの認証に適した代替手段がない可能性があります。Cookie を使用する代わりに、STOMP メッセージングプロトコルレベルのヘッダーで認証することを好む場合があります。これを行うには、2 つの簡単な手順が必要です。

  1. STOMP クライアントを使用して、接続時に認証ヘッダーを渡します。

  2. 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 の前にあることを確認する必要があることに注意してください。これは、@Order(Ordered.HIGHEST_PRECEDENCE + 99) でマークされた WebSocketMessageBrokerConfigurer の独自の実装でカスタムインターセプターを宣言することで最適に実行されます。