问题
有没有一种方法可以全局处理由Spring WebSocket模块中的错误(通常是权限不足)引起的Spring Messaging MessageDeliveryException
?
用例
我已经实现了基于STOMP的Spring WebSockets,以支持我的Web应用程序中的ws连接。为了保护websocket端点,我创建了一个拦截器,在STOMP CONNECT时间授权用户启动STOMP会话(如Spring文档22.4.11部分中建议的那样):
@Component
public class StompMessagingInterceptor extends ChannelInterceptorAdapter {
// Some code not important to the problem
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor headerAccessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
switch (headerAccessor.getCommand()) {
// Authenticate STOMP session on CONNECT using jwt token passed as a STOMP login header - it's working great
case CONNECT:
authorizeStompSession(headerAccessor);
break;
}
// Returns processed message
return message;
}
// Another part of code not important for the problem
}
我已经添加了spring-security-messaging配置,以在消息传递时对权限进行精细控制:
@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.simpTypeMatchers(
SimpMessageType.CONNECT,
SimpMessageType.DISCONNECT,
SimpMessageType.HEARTBEAT
).authenticated()
.simpSubscribeDestMatchers("/queue/general").authenticated()
.simpSubscribeDestMatchers("/user/queue/priv").authenticated()
.simpDestMatchers("/app/general").authenticated()
.simpDestMatchers("/user/*/queue/priv").hasAuthority("ADMIN")
.anyMessage().denyAll();
}
@Override
protected boolean sameOriginDisabled() {
return true;
}
}
首先 - 这个配置按预期工作,问题在于当websocket通信过程中发生某些安全异常时(例如没有管理员权限的用户尝试在“/user/{something}/queue/priv”端点上发送消息),会导致org.springframework.messaging.MessageDeliveryException
抛出,并且:
- 完整的异常堆栈跟踪被写入我的服务器日志中
- 返回STOMP ERROR帧,其中包含部分堆栈跟踪作为其
message
字段。
我想要做的是捕获(如果可能的话全局捕获)DeliveryException
,检查是什么原因引起了它,然后相应地创建自己的消息以在STOMP ERROR帧中返回(比如使用一些错误代码,例如403来模拟HTTP)并且而不是继续抛出原始异常,只需使用我的记录器记录一些警告。这可行吗?
我尝试过的
在寻找解决方案时,我发现有些人使用@MessageExceptionHandler
来捕获消息异常。Spring 4.2.3(我使用的版本)文档只在这里的25.4.11节中提到了它一次。我尝试像这样使用它:
@Controller
@ControllerAdvice
public class WebSocketGeneralController {
...
@MessageExceptionHandler
public WebSocketMessage handleException(org.springframework.messaging.MessageDeliveryException e) {
WebSocketMessage errorMessage = new WebSocketMessage();
errorMessage.setMessage(e.getClass().getName());
return errorMessage;
}
}
但似乎该方法在任何时候都未被调用(尝试捕获不同异常,包括 Exception
- 没有结果)。我还应该查看什么?