spring-boot - 在 stomp websocket spring boot 2 中自定义错误消息
问题描述
我使用 Spring Boot 2 WebSocket + SockJS + STOMP。
ChannelInterceptor
当客户端在preSend
方法中连接时,我有 JWT 授权。
@Component
public class WebSocketChannelInterceptor implements ChannelInterceptor {
private final TokenUtils tokenUtils;
private final AuthenticationManager websocketAuthenticationManager;
private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketChannelInterceptor.class);
public WebSocketChannelInterceptor(TokenUtils tokenUtils, AuthenticationManager websocketAuthenticationManager) {
this.tokenUtils = tokenUtils;
this.websocketAuthenticationManager = websocketAuthenticationManager;
}
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
LOGGER.info("WEBSOCKETCHANNELINTERCEPTOR -> "+message.toString());
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (accessor != null && StompCommand.CONNECT.equals(accessor.getCommand())) {
List<String> headers = accessor.getNativeHeader(AUTHORIZATION);
accessor.setUser(websocketAuthenticationManager.authenticate(new JWTTokenAuthentication(tokenUtils.resolveToken(headers != null ? headers.get(0) : null))));
}
return message;
}
@Override
public boolean preReceive(MessageChannel channel) {
LOGGER.info("preReceive");
return true;
}
}
身份验证管理器:
@Component("websocketAuthenticationManager")
@AllArgsConstructor
public class WebsocketAuthenticationManager implements AuthenticationManager {
private final TokenUtils tokenUtils;
private final ClientRepository clientRepository;
private final OperatorRepository operatorRepository;
@Override
@Transactional(readOnly = true)
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
JWTTokenAuthentication jwtTokenAuthentication = (JWTTokenAuthentication) authentication;
String token = jwtTokenAuthentication.getToken();
if(tokenUtils.validateToken(token)) {
RoleType role = RoleType.get(tokenUtils.get("role", token));
if(role == null) {
throw throwBadCredentialsException("Unknown role");
}
switch (role) {
case OPERATOR:
Optional<OperatorEntity> operator = operatorRepository.findByLogin(tokenUtils.get("login", token));
if(operator.isPresent()) {
return new WebsocketOperatorDetails(token, OperatorDetails.builder().role(role.toString()).operator(operator.get()).build());
} else {
throw throwBadCredentialsException("Operator not found");
}
case CLIENT:
Optional<ClientEntity> client = clientRepository.findByHash(tokenUtils.get("hash", token));
if(client.isPresent()) {
return new WebsocketClientDetails(token, ClientDetails.builder().role(role.toString()).client(client.get()).build());
} else {
throw throwBadCredentialsException("Client not found");
}
case OWNER:
throw throwBadCredentialsException("Access denied");
default:
throw throwBadCredentialsException("Unknown role");
}
} else {
throw throwBadCredentialsException("Invalid token");
}
}
private MessagingException throwBadCredentialsException(String message) {
Map<String, Object> headers = new HashMap<>();
headers.put("status", StatusType.UNAUTHORIZED);
headers.put("message", message);
return new MessagingException(new MutableMessage<>(new MessageHeaders(headers)));
}
}
在 AuthenticationManager 中,如果出现错误,我会抛出错误。
在客户端,我得到下一个内容:
{
"command":"ERROR",
"headers":{
"content-length":"0",
"message":"Failed to send message to ExecutorSubscribableChannel[clientInboundChannel]; nested exception is org.springframework.security.authentication.AuthenticationCredentialsNotFoundException\\c Invalid token"
},
"_binaryBody":{
},
"isBinaryBody":true,
"escapeHeaderValues":false,
"skipContentLengthHeader":false,
"_body":""
}
如何更改代码以发送带有有效负载的错误消息?
谢谢。
解决方案
STOMP 注册表中有一个专用的设置器:
@Configuration
@EnableWebSocketMessageBroker
class WebSocketConfiguration : WebSocketMessageBrokerConfigurer {
override fun configureMessageBroker(registry: MessageBrokerRegistry) {
// ...
}
override fun registerStompEndpoints(registry: StompEndpointRegistry) {
registry.addEndpoint("/ws")
// Handle exceptions in interceptors and Spring library itself.
// Will terminate a connection and send ERROR frame to the client.
registry.setErrorHandler(object : StompSubProtocolErrorHandler() {
override fun handleInternal(
errorHeaderAccessor: StompHeaderAccessor,
errorPayload: ByteArray,
cause: Throwable?,
clientHeaderAccessor: StompHeaderAccessor?
): Message<ByteArray> {
errorHeaderAccessor.message = null
val message = "..."
return MessageBuilder.createMessage(message.toByteArray(), errorHeaderAccessor.messageHeaders)
}
})
}
}
推荐阅读
- python - 熊猫如何比较2个数据帧的行而不考虑顺序
- jquery - 无法在滚动事件侦听器Angular 4中执行组件的功能
- python-3.x - 用于检索子进程输出的python代码(linux ps命令)
- git - Git cherry-pick 到集成分支,然后从功能分支恢复原始提交
- php - Laravel 的“广播”功能什么都不做
- java - 构建项目时捕获 AndroidLocationException
- angular5 - Angular 5:表单数组中的表单组内的嵌套表单控件
- java - 带有 DialogInterface 的嵌套开关
- typescript - Webpack 导出模块未定义
- php - PHP将3位数字除以一位