令牌认证
Spring Security OAuth 提供了对基于令牌的安全性的支持,包括JSON Web令牌 (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。这样做需要两个简单的步骤:
-
使用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)
标记。