我有一个Java套接字API应用程序,它处理来自用户的套接字请求并发送响应。我有一个配置器:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
private static final Logger LOGGER = Logger.getLogger(WebSocketConfig.class);
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/queue");
config.setApplicationDestinationPrefixes("/server_in");
config.setUserDestinationPrefix("/user");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket").withSockJS();
}
}
当我向用户发送回复时,我会执行以下操作:this.simpMessagingTemplate.convertAndSend("/queue/private/user_"+secret_key, socketResponse);
在客户端上我有以下代码:
sc.subscribe('/queue/private/user_'+secret_key, function (greeting) {
console.log(greeting.body);
});
响应处理成功。但问题是其他一些用户也可以订阅“/ queue / private / *”目的地并处理私人消息。
sc.subscribe('/queue/private/*', function (greeting) {
console.log(greeting.body);
});
我该如何防止这种行为?
如果您希望每个用户都有一个套接字并且只向他发送消息,那么您可以做的是:
像对待endPoint一样订阅,但例如使用“/ user”infront
sc.subscribe('/user/queue/websocket, function (greeting) {
console.log(greeting.body);
});
在服务器端你应该有一个休息方法:
@RequestMapping(value = "/test", method = RequestMethod.POST)
public void test(Principal principal) throws Exception {
this.template.convertAndSendToUser(principal.getName(), "/queue/click", "");
}
有了这个,每个用户都会收到每个频道的用户,并且只有用户会在进行休息呼叫时收到通知。
其余的调用应该进行身份验证,以便Principal拥有用户名。
用户通道是从Spring自动管理的,因此您必须像这样添加它。
您可以扩展ChannelInterceptorAdapter并单独管理每个事件:
public class AuthorizedChannelInterceptorAdapter extends ChannelInterceptorAdapter {
@Override
public Message<?> preSend(Message<?> message, MessageChannel messageChannel) throws AuthenticationException {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT == accessor.getCommand())
setUserAuthenticationToken(accessor);
else if (StompCommand.SUBSCRIBE == accessor.getCommand())
validateSubscription((Authentication) accessor.getUser(), accessor.getDestination());
return message;
}
private void setUserAuthenticationToken(StompHeaderAccessor accessor) {
String token = accessor.getFirstNativeHeader(HttpHeaders.AUTHORIZATION);
accessor.setUser(loadAuthentication(token));
}
private Authentication loadAuthentication(String token){
return ....;
}
private void validateSubscription(Authentication authentication, String destination) {
if(...)
throw new AccessDeniedException("No permission to subscribe to this topic");
}
}
首先,您需要在连接事件中存储客户端提供的身份验证对象。在此之后,客户端发送的每个事件都将设置此身份验证对象,以便您可以使用它来验证是否有权订阅特定通道。