我在javascript客户端使用stomp.js而不是SockJS。我正在使用
连接到websocketstompClient.connect({}, function (frame) {
践踏sockJS连接有2个http请求:
- 请求/info
客户端发送所有cookie。我也想发送自定义头(例如XSRF头),但没有找到这样做的方法。
@Rohitdev所以基本上你不能使用stompClient发送任何HTTP报头,因为STOMP是在websockets上的层,只有当websockets握手发生时,我们才有可能发送自定义报头。所以只有SockJS可以发送这个头,但由于某些原因不这样做:https://github.com/sockjs/sockjs-client/issues/196
自定义标题:
stompClient.connect({token: "ABC123"}, function(frame) { ... code ...});
不带自定义头:
stompClient.connect({}, function(frame) { ... code ...});
在Javascript中,您可以使用以下命令提取STOMP标头:
username = frame.headers['user-name'];
在服务器端,如果你正在使用Spring框架,你可以实现一个拦截器来复制HTTP参数到WebSockets STOMP头。
public class HttpSessionHandshakeInterceptor_personalised implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
// Set ip attribute to WebSocket session
attributes.put("ip", request.getRemoteAddress());
// ============================================= CODIGO PERSONAL
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpServletRequest httpServletRequest = servletRequest.getServletRequest();
// httpServletRequest.getCookies();
// httpServletRequest.getParameter("inquiryId");
// httpServletRequest.getRemoteUser();
String token = httpServletRequest.getParameter("token");
...
}
}
对于没有STOMP参数的发送消息:
function sendMessage() {
var from = document.getElementById('from').value;
var text = document.getElementById('text').value;
stompClient.send("/app/chatchannel", {},
JSON.stringify({'from':from, 'text':text}));
}
,这里你是在传递参数到STOMP报头。
function sendMessage() {
var from = document.getElementById('from').value;
var text = document.getElementById('text').value;
stompClient.send("/app/chatchannel", {'token':'AA123'},
JSON.stringify({'from':from, 'text':text}));
}
如果您在服务器上使用Spring引导,则在方法中使用@Header(name = "token")
注释
用法-
@Controller
public class SocketController {
static final String token = "1234";
@MessageMapping("/send")
@SendTo("/receive/changes")
public Object notify(MessageModel message, @Header(name = "token") String header)throws Exception {
if(!header.equals(token)) {
// return when headers do not match
return("Unauthorized");
}
// return the model object with associated sent message
return new MessageModel(message.getMessage());
}
}
你应该有一个MessageModel
类与message
变量和所需的getters
, setters
和contructor
。
前端使用Stomp
使用function sendMessage() {
var text = document.getElementById('text').value;
stompClient.send("/send/message", {'token':'1234'},
JSON.stringify({'message':text}));
}
要增加更多的安全性,您可以在Spring中使用CORS
您必须使用query作为参数,而不是在Header中使用Authorization。(? = token_2222)查询例如:var socket = new SockJS('/ws?query=token_2222');然后在HandshakeInterceptor中读到Sergio写的
SockJS JavaScript客户端不支持在发送SockJS请求时发送授权头。
Spring Java的STOMP客户端允许设置握手头:
WebSocketHttpHeaders handshakeHeaders = new WebSocketHttpHeaders();
handshakeHeaders.add(principalRequestHeader, principalRequestValue);
附加信息:https://www.toptal.com/java/stomp-spring-boot-websocket
你可以在Spring文档中找到很多关于这一点的信息:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html websocket-stomp-authentication
简短总结:对于使用cookie的应用程序,集成非常好(Spring Security和其他)。
对于使用JWT的应用程序,可能的选项是:
1。在DefaultHandshakeHandler
的实现中添加一个请求参数和进程var socket = new SockJS('/our-websocket?query=token_2222');
2。或者直接添加到消息的STOMP头:
//添加
var headers = {
login: 'mylogin',
passcode: 'mypasscode',
// additional header
'client-id': 'my-client-id'
};
stompClient.connect(headers, function (frame) {}
//处理地点
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(final MessageBrokerRegistry registry) {
registry.enableStompBrokerRelay("/topic")
.setRelayHost("127.0.0.1")
.setRelayPort(61613) //rabbitmq-plugins enable rabbitmq_stomp ; docker exec -it ID bash
.setClientLogin("guest")
.setClientPasscode("guest")
.setUserRegistryBroadcast("/topic/registry") //позволяет отправлять сообщеня всем приватные сообщения всем юзерам
;
registry.setApplicationDestinationPrefixes("/ws");
}
@Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
registry.addEndpoint("/our-websocket")
.setHandshakeHandler(new UserHandshakeHandler())
.setAllowedOriginPatterns("*")
.withSockJS()
;
}
@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())) {
// 1) Perform authentication here using standard authentication providers (managers).
//2) Install the user in case of successful authentication or throw an error
accessor.setUser(new UserPrincipal("TEST USER 2"));
}
return message;
}
});
}
}