令牌认证

Spring Security OAuth 提供了基于令牌的安全支持,包括 JSON Web Token (JWT)。您可以将其用作 Web 应用程序中的身份验证机制,包括 WebSocket 上的 STOMP 交互,如上一节所述(即,通过基于 Cookie 的会话维护身份)。

同时,基于 Cookie 的会话并不总是最合适的(例如,在不维护服务器端会话的应用程序中,或在移动应用程序中,通常使用标头进行身份验证)。

WebSocket 协议,RFC 6455“没有规定服务器在 WebSocket 握手期间对客户端进行身份验证的任何特定方式”。然而,在实践中,浏览器客户端只能使用标准身份验证标头(即,基本的 HTTP 身份验证)或 Cookie,而不能(例如)提供自定义标头。同样,SockJS JavaScript 客户端不提供发送带有 SockJS 传输请求的 HTTP 标头的方式。请参阅 sockjs-client 问题 196。相反,它允许发送可用于发送令牌的查询参数,但这有其自身的缺点(例如,令牌可能会无意中随 URL 一起记录在服务器日志中)。

上述限制适用于基于浏览器的客户端,不适用于基于 Spring Java 的 STOMP 客户端,该客户端确实支持发送带有 WebSocket 和 SockJS 请求的标头。

因此,希望避免使用 Cookie 的应用程序可能无法在 HTTP 协议级别找到任何好的身份验证替代方案。它们可能更喜欢在 STOMP 消息协议级别使用标头进行身份验证,而不是使用 Cookie。这样做需要两个简单的步骤

  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 之前执行。最好通过在自己的 WebSocketMessageBrokerConfigurer 实现中声明自定义拦截器来完成此操作,该实现标记为 @Order(Ordered.HIGHEST_PRECEDENCE + 99)

© . This site is unofficial and not affiliated with VMware.