简体   繁体   English

SpringMVC Websockets使用Spring Security进行消息传递用户身份验证

[英]SpringMVC Websockets Messaging User Authentication with Spring Security

I have seen a couple of threads about this issue, but none of them seem to really answer the question directly. 我已经看到了关于这个问题的几个主题,但它们似乎都没有直接回答这个问题。

Background, I have spring security installed, working, and running smoothly in other parts of the application. 背景,我在应用程序的其他部分安装了弹簧安全,工作和运行。 My username is "developer". 我的用户名是“开发者”。

Running on Java 7, Glassfish 4, Spring 4, and using Angular + StompJS 在Java 7,Glassfish 4,Spring 4上运行,并使用Angular + StompJS

Let's get some code here: 我们在这里得到一些代码:

package com.myapp.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketBrokerConfig extends AbstractWebSocketMessageBrokerConfigurer {

    public final static String userDestinationPrefix = "/user/";

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/stomp").withSockJS().setSessionCookieNeeded(true);
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.setApplicationDestinationPrefixes("/app");
        //registry.enableStompBrokerRelay("/topic,/user");
        registry.enableSimpleBroker("/topic", "/user");
        registry.setUserDestinationPrefix(userDestinationPrefix);


    }


}

Ok, now here is a controller, to send out stuff every 3 seconds: 好的,现在这里是一个控制器,每隔3秒发送一次东西:

import org.springframework.messaging.simp.SimpMessagingTemplate;

…

@Autowired
private SimpMessagingTemplate messagingTemplate;

…

@Scheduled(fixedDelay = 3000)
public void sendStuff () 
{

    Map<String, Object> map = new HashMap<>();
    map.put(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON);
    System.out.print("Sending data! " + System.currentTimeMillis());
    //messagingTemplate.convertAndSend("/topic/notify", "Public: " + System.currentTimeMillis());

    messagingTemplate.convertAndSendToUser("developer", "/notify", "User: " + System.currentTimeMillis());
    messagingTemplate.convertAndSendToUser("notYou", "/notify", "Mr Developer Should Not See This: " + System.currentTimeMillis());
}

And finally the JavaScript using SockJS 最后是使用SockJS的JavaScript

    var client = new SockJS('/stomp');
    var stomp = Stomp.over(client);
    stomp.connect({}, function(s) {

        //This should work
        stomp.subscribe('/user/' + s.headers['user-name'] + '/notify', console.debug);

        //This SHOULD NOT
        stomp.subscribe('/user/notYou/notify', console.debug);

    });
    client.onclose = $scope.reconnect;

And finally, for kicks, the pom.xml 最后,对于踢,pom.xml

    <dependency>
        <groupId>javax.websocket</groupId>
        <artifactId>javax.websocket-api</artifactId>
        <version>1.0</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-messaging</artifactId>
        <version>4.0.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-websocket</artifactId>
        <version>4.0.6.RELEASE</version>
    </dependency>

Here is what does work: 这是做什么的:

  1. I can produce wonderfully communication back and forth between the client and the server 我可以在客户端和服务器之间来回产生奇妙的通信
  2. It's fast 它很快
  3. messagingTemplate.convertAndSend and messagingTemplate.convertAndSendToUser messagingTemplate.convertAndSendmessagingTemplate.convertAndSendToUser

This is the problem (noted above): Anyone can subscribe to other users feeds. 这是问题(如上所述):任何人都可以订阅其他用户的订阅源。

Now, there are a few other versions of this floating around, I will list them below, and explain why the answers are all wrong: 现在,还有其他几个版本,我将在下面列出它们,并解释为什么答案都是错误的:

What are the security issues around an open websocket connection? 开放式websocket连接的安全问题是什么?

Spring websocket with stomp security - every user can subscribe to any other users queue? 具有stomp安全性的Spring websocket - 每个用户都可以订阅任何其他用户队列?

Websocket: How To Push A Message To A Target User Websocket:如何将消息推送给目标用户

Here's the problem: 这是问题所在:

  • Look at messagingTemplate.convertAndSendToUser - All that does is add the "user prefix" and then the username provided and then use messagingTemplate.convertAndSend which does not apply security. 查看messagingTemplate.convertAndSendToUser - 只需添加“用户前缀”然后添加用户名,然后使用不应用安全性的messagingTemplate.convertAndSend

  • Then people say that "you need to use spring security just like everywhere else" - the problem here is A) that I am SENDING data to the client asynchronously, so B) I will be using this code completely outside of the user's session, possibly from a different user (say to send a notification to another logged in user). 然后人们说“你需要像其他地方一样使用Spring安全” - 这里的问题是A)我正在异步地向客户端发送数据,所以B)我将完全在用户会话之外使用这段代码,可能来自不同的用户(比如向另一个登录用户发送通知)。

Let me know if this is too closely related to a different post, but I this is a big problem for me and I wanted to do this justice. 让我知道这是否与一个不同的帖子关系太密切,但我这对我来说是一个大问题,我想做到这一点。

I can get more details though if anyone needs more details. 如果有人需要更多细节,我可以获得更多细节。

New Spring Security 4x now fully support Web Socket, you can refer the link Preview Spring Security WebSocket Support 新的Spring Security 4x现在完全支持Web Socket,您可以参考预览Spring Security WebSocket Support链接

Or SpringSecuritySupportWebSocket.html in case you need a complete example, SpringSecuritySupportWebSocket.html ,以防您需要完整的示例,

I think you must make these changes: 我想你必须做出这些改变:

1) You must not enableSimpleBroker for "/user" because it's a special queue handled automatically by the broker 1)您不能为“/ user”启用SimpleBroker,因为它是由代理自动处理的特殊队列

2) if the server uses for example the annotation "@SendToUser("/queue/private")" the client must subscribe to the queue "/user/queue/private" : you must not prepend the username in the queue because it's a transparent operation handled by the broker 2)如果服务器使用例如注释“@SendToUser(”/ queue / private“)”客户端必须订阅队列“/ user / queue / private”:你不能在队列中添加用户名,因为它是一个经纪人处理的透明操作

I'm sure this works correctly because I'm using it in my setup. 我确信这是正常的,因为我在我的设置中使用它。

I've not tried with the convertAndSendToUser() method but since its semantic should be the same of the annotation, it should work too. 我没有尝试过convertAndSendToUser()方法,但由于它的语义应该与注释相同,所以它也应该工作。

You can override configureInbound method in a JavaConfig class extending AbstractSecurityWebSocketMessageBrokerConfigurer. 您可以在扩展AbstractSecurityWebSocketMessageBrokerConfigurer的JavaConfig类中覆盖configureInbound方法。

@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
    messages
            .nullDestMatcher().authenticated() 1
            .simpSubscribeDestMatchers("/user/queue/errors").permitAll() 2
            .simpDestMatchers("/app/**").hasRole("USER") 3
            .simpSubscribeDestMatchers("/user/**", "/topic/friends/*").hasRole("USER") 4
            .simpTypeMatchers(MESSAGE, SUBSCRIBE).denyAll() 5
            .anyMessage().denyAll(); 6

    }
}

There, you can configure credentials to subscribe a channel, send messages or several other things, as mentioned in Spring WebSocket documntation https://docs.spring.io/spring-security/site/docs/current/reference/html/websocket.html#websocket-authorization 在那里,您可以配置凭据以订阅频道,发送消息或其他一些内容,如Spring WebSocket documntation https://docs.spring.io/spring-security/site/docs/current/reference/html/websocket中所述。 HTML#WebSocket的授权

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM