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

时间:2014-07-28 20:47:51

标签: java spring spring-mvc spring-security websocket

我已经看到了关于这个问题的几个主题,但它们似乎都没有直接回答这个问题。

后台,我在应用程序的其他部分安装了弹簧安全,工作和运行。我的用户名是"开发人员"。

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

让我们在这里获得一些代码:

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);


    }


}

好的,现在这里是一个控制器,每隔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());
}

最后使用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;

最后,对于踢,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>

以下是有效的方法:

  1. 我可以在客户端和服务器之间来回产生奇妙的通信
  2. 快速
  3. messagingTemplate.convertAndSendmessagingTemplate.convertAndSendToUser
  4. 这是问题(如上所述):任何人都可以订阅其他用户供稿。

    现在,还有其他一些版本,我将在下面列出它们,并解释为什么答案都是错误的:

    What are the security issues around an open websocket connection?

    Spring websocket with stomp security - every user can subscribe to any other users queue?

    Websocket: How To Push A Message To A Target User

    问题在于:

    • 查看messagingTemplate.convertAndSendToUser - 所有这一切都是添加&#34;用户前缀&#34;然后使用提供的用户名,然后使用不适用安全性的messagingTemplate.convertAndSend

    • 然后人们说&#34;你需要像其他地方一样使用春季安全&#34; - 这里的问题是A)我正在异步地向客户端发送数据,因此B)我将完全在用户会话之外使用此代码,可能来自不同的用户(比如向另一个用户发送通知)登录用户)。

    让我知道这是否与其他职位关系过于密切,但我这对我来说是个大问题,我想做到这一点。

    如果有人需要更多细节,我可以获得更多细节。

3 个答案:

答案 0 :(得分:8)

New Spring Security 4x现在完全支持Web Socket,您可以参考链接Preview Spring Security WebSocket Support

SpringSecuritySupportWebSocket.html如果您需要完整的示例,

答案 1 :(得分:6)

我认为你必须做出这些改变:

1)您不得为&#34; / user&#34;启用SimpleBroker;因为它是经纪人自动处理的特殊队列

2)如果服务器使用例如注释&#34; @SendToUser(&#34; / queue / private&#34;)&#34;客户端必须订阅队列&#34; / user / queue / private&#34; :您不能在队列中添加用户名,因为它是由代理处理的透明操作

我确定这是正常的,因为我在我的设置中使用它。

我没有尝试使用convertAndSendToUser()方法,但由于它的语义应该与注释相同,所以它也应该有效。

答案 2 :(得分:4)

您可以在扩展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

    }
}

在那里,您可以配置凭据以订阅频道,发送消息或其他一些内容,如Spring WebSocket documntation中提到的https://docs.spring.io/spring-security/site/docs/current/reference/html/websocket.html#websocket-authorization