Token Authentication
Spring Security OAuth 提供基于Tokens的安全支持,包括JSON Web Token(JWT)。 您可以将其用作Web应用程序中的身份验证机制, 包括WebSocket上的STOMP交互,如上一节所述 (即通过基于cookie的会话来维持身份)。
同时,基于 cookie 的会话并不总是最佳选择(例如,在不维护服务器端会话的应用程序中,或者在移动应用程序中,通常使用头信息进行身份验证)。
The WebSocket协议,RFC 6455 “并未规定服务器在WebSocket握手期间对客户端进行身份验证的特定方式。” 实际上,浏览器客户端只能使用标准的身份验证头(即基本HTTP身份验证)或cookies,而不能(例如)提供自定义头。同样,SockJS JavaScript客户端也无法在SockJS传输请求中发送HTTP头。请参见 sockjs-client问题196。 相反,它允许发送查询参数,您可以使用这些参数发送Tokens,但这也有其自身的缺点(例如,Tokens可能会意外地与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) 标记。