令牌认证

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消息传递协议级别上使用标头进行身份验证。这样做需要两个简单的步骤:

  1. 在连接时使用STOMP客户端传递身份验证标头。

  2. 使用ChannelInterceptor处理身份验证标头。

下一个示例使用服务器端配置来注册自定义身份验证拦截器。请注意,拦截器只需要对CONNECT Message进行身份验证并设置用户标头。Spring会注意并保存经过身份验证的用户,并将其与同一会话上的后续STOMP消息关联起来。以下示例显示了如何注册自定义身份验证拦截器:

@Configuration
@EnableWebSocketMessageBroker
public class MyConfig 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())) {
					Authentication user = ... ; // 访问身份验证标头
					accessor.setUser(user);
				}
				return message;
			}
		});
	}
}

此外,请注意,当您使用Spring Security对消息进行授权时,目前需要确保身份验证ChannelInterceptor配置在Spring Security之前。最好的方法是通过在自己的WebSocketMessageBrokerConfigurer实现中声明自定义拦截器,并标记为@Order(Ordered.HIGHEST_PRECEDENCE + 99)