如果资源服务器应该是无状态的,如何使用 websocket 将消息发送到队列
If resource server are supposed to be stateless, how to send message to queue with websocket
我目前在消息传递系统中工作,其中资源服务器使用 oAuth 2 是无状态的。现在,我必须使用队列向单个用户发送消息,但问题是 spring 消息传递需要一个会话other 发送消息,如 .
中所述
我的问题是如何在无状态 restful 服务中获取当前登录用户:
@MessageMapping("/messaging")
public void messaging( Message<Object> message) {
Principal user=
message.getHeaders()
.get(SimpMessageHeaderAccessor.USER_HEADER,Principal.class);
messageTemplate.convertAndSend("/topic/users", user.getName());
}
当我们使用 simpMessagingTemplate.convertAndSendToUser(...) 方法并传递与会话 ID 关联的用户名时,Spring 将使用队列。否则它将使用一个主题,所有订阅的客户端最终将读取从服务器返回的相同消息。
因为我在资源服务器中没有会话,需要排队向个人发送消息user.Any 评论和想法表示赞赏
终于找到解决办法了。通过为用户名解码 json 网络令牌并为用户名提供身份验证解决上述问题。 JwtAuthentication 是自定义的 class,它负责解码 JWT 并为 JWT
的用户名提供身份验证
@Configuration
@EnableWebSocketMessageBroker
@Order(Ordered.HIGHEST_PRECEDENCE + 99)
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Autowired
private SimpUserRegistry userRegistry;
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic","/queue");
// use the /app prefix for others
config.setApplicationDestinationPrefixes("/app");
}
@Autowired
private JwtAuthentication jwtAuthentication;
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// use the /messaging endpoint (prefixed with /app as configured above) for incoming requests
registry.addEndpoint("/messaging").setAllowedOrigins("http://localhost:8080").withSockJS();
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.setInterceptors(new ChannelInterceptorAdapter() {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor =
MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
List<String> tokenList = accessor.getNativeHeader("Authorization");
String token = null;
if(tokenList != null && tokenList.size() > 0) {
token = tokenList.get(0).replaceAll("Bearer", "").trim();
}
if (StompCommand.CONNECT.equals(accessor.getCommand()) || StompCommand.SUBSCRIBE.equals(accessor.getCommand()) || StompCommand.SEND.equals(accessor.getCommand()) ) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if(auth==null){
Authentication user = jwtAuthentication.getAuthentication(token); // access authentication header(s)
SecurityContextHolder.getContext().setAuthentication(user);
((DefaultSimpUserRegistry) userRegistry).onApplicationEvent(new SessionConnectedEvent(this, (Message<byte[]>) message, auth));
accessor.setUser(user);
} else {
accessor.setUser(auth);
((DefaultSimpUserRegistry) userRegistry).onApplicationEvent(new SessionConnectedEvent(this, (Message<byte[]>) message, auth));
}
}
accessor.setLeaveMutable(true);
return MessageBuilder.createMessage(message.getPayload(), accessor.getMessageHeaders());
}
});
}
}
在应用程序上下文中,我们需要注册 SimpUserRegistry
@豆
@基本的
public SimpUserRegistry userRegistry() {
return 新的 DefaultSimpUserRegistry();
}
@Bean
@Primary
public UserDestinationResolver userDestinationResolver() {
return new DefaultUserDestinationResolver(userRegistry());
}
现在我们可以向特定用户发送消息
public void handle(Exchange exchange) {
Message camelMessage = exchange.getIn();
com.livetalk.user.utils.Message message = camelMessage.getBody( com.livetalk.user.utils.Message.class);
// send the message specifically to the destination user by using STOMP's user-directed messaging
msgTemplate.convertAndSendToUser(message.getRecipient(), "/queue/messages", message, defaultHeaders);
}
我目前在消息传递系统中工作,其中资源服务器使用 oAuth 2 是无状态的。现在,我必须使用队列向单个用户发送消息,但问题是 spring 消息传递需要一个会话other 发送消息,如 .
中所述我的问题是如何在无状态 restful 服务中获取当前登录用户:
@MessageMapping("/messaging")
public void messaging( Message<Object> message) {
Principal user=
message.getHeaders()
.get(SimpMessageHeaderAccessor.USER_HEADER,Principal.class);
messageTemplate.convertAndSend("/topic/users", user.getName());
}
当我们使用 simpMessagingTemplate.convertAndSendToUser(...) 方法并传递与会话 ID 关联的用户名时,Spring 将使用队列。否则它将使用一个主题,所有订阅的客户端最终将读取从服务器返回的相同消息。
因为我在资源服务器中没有会话,需要排队向个人发送消息user.Any 评论和想法表示赞赏
终于找到解决办法了。通过为用户名解码 json 网络令牌并为用户名提供身份验证解决上述问题。 JwtAuthentication 是自定义的 class,它负责解码 JWT 并为 JWT
的用户名提供身份验证@Configuration
@EnableWebSocketMessageBroker
@Order(Ordered.HIGHEST_PRECEDENCE + 99)
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Autowired
private SimpUserRegistry userRegistry;
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic","/queue");
// use the /app prefix for others
config.setApplicationDestinationPrefixes("/app");
}
@Autowired
private JwtAuthentication jwtAuthentication;
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// use the /messaging endpoint (prefixed with /app as configured above) for incoming requests
registry.addEndpoint("/messaging").setAllowedOrigins("http://localhost:8080").withSockJS();
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.setInterceptors(new ChannelInterceptorAdapter() {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor =
MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
List<String> tokenList = accessor.getNativeHeader("Authorization");
String token = null;
if(tokenList != null && tokenList.size() > 0) {
token = tokenList.get(0).replaceAll("Bearer", "").trim();
}
if (StompCommand.CONNECT.equals(accessor.getCommand()) || StompCommand.SUBSCRIBE.equals(accessor.getCommand()) || StompCommand.SEND.equals(accessor.getCommand()) ) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if(auth==null){
Authentication user = jwtAuthentication.getAuthentication(token); // access authentication header(s)
SecurityContextHolder.getContext().setAuthentication(user);
((DefaultSimpUserRegistry) userRegistry).onApplicationEvent(new SessionConnectedEvent(this, (Message<byte[]>) message, auth));
accessor.setUser(user);
} else {
accessor.setUser(auth);
((DefaultSimpUserRegistry) userRegistry).onApplicationEvent(new SessionConnectedEvent(this, (Message<byte[]>) message, auth));
}
}
accessor.setLeaveMutable(true);
return MessageBuilder.createMessage(message.getPayload(), accessor.getMessageHeaders());
}
});
}
}
在应用程序上下文中,我们需要注册 SimpUserRegistry @豆 @基本的 public SimpUserRegistry userRegistry() { return 新的 DefaultSimpUserRegistry(); }
@Bean
@Primary
public UserDestinationResolver userDestinationResolver() {
return new DefaultUserDestinationResolver(userRegistry());
}
现在我们可以向特定用户发送消息
public void handle(Exchange exchange) {
Message camelMessage = exchange.getIn();
com.livetalk.user.utils.Message message = camelMessage.getBody( com.livetalk.user.utils.Message.class);
// send the message specifically to the destination user by using STOMP's user-directed messaging
msgTemplate.convertAndSendToUser(message.getRecipient(), "/queue/messages", message, defaultHeaders);
}