【问题标题】:SpringMVC Websockets Messaging User Authentication with Spring SecuritySpringMVC Websockets 使用 Spring Security 进行消息传递用户身份验证
【发布时间】:2014-09-20 03:53:41
【问题描述】:

我看过几个关于这个问题的帖子,但似乎没有一个能真正直接回答这个问题。

背景,我已经在应用程序的其他部分安装、工作和顺利运行 spring security。我的用户名是“开发者”。

在 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

这就是问题(上面提到的):任何人都可以订阅其他用户的订阅源。

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

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 - 只需添加“用户前缀”和提供的用户名,然后使用不适用安全性的messagingTemplate.convertAndSend

  • 然后人们说“你需要像其他任何地方一样使用 Spring Security”——这里的问题是 A)我正在异步向客户端发送数据,所以 B)我将完全在外部使用此代码用户的会话,可能来自不同的用户(比如向另一个登录用户发送通知)。

如果这与其他帖子的关系过于密切,请告诉我,但这对我来说是个大问题,我想公正地做到这一点。

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

【问题讨论】:

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


    【解决方案1】:

    新的 Spring Security 4x 现在全面支持 Web Socket,可以参考链接Preview Spring Security WebSocket Support

    或者SpringSecuritySupportWebSocket.html,如果你需要一个完整的例子,

    【讨论】:

    • 男孩几个月前会很好! :)
    • 我仍然认为这不能解决@Nitroware 问题,顺便说一句,这也是我的问题。 phuong 给出的示例将基于 ROLE 的权限添加到队列中,但不会阻止 ROLE X 中的用户 A 订阅 ROLE X 中另一个用户 B 的消息。我认为这个问题仍然存在。当用户订阅消息时需要一个授权钩子
    • @nsdiv 您可以使用类似 messages.simpSubscribeDestMatchers("/subscribe").access("@beanName.check(authentication,message)") 这将使用名称为 beanName 的 Spring Bean 和传入当前的身份验证和 WebSocket 消息。您实现 check 方法以返回 true 或 false。如果您想了解更多详细信息,则需要创建一个新问题,因为评论部分有限
    • @RobWinch:创建了一个新问题 - stackoverflow.com/questions/34096412/…
    • @phuong 第二个链接坏了
    【解决方案2】:

    您可以在扩展 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 文档https://docs.spring.io/spring-security/site/docs/current/reference/html/websocket.html#websocket-authorization 中所述

    【讨论】:

    • 这其实是正确的答案。有了这一点,如果其他用户尝试订阅广播目的地 /topic/queue 或任何不允许明确的目的地,他们会收到错误。
    【解决方案3】:

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

    1) 您不能为“/user”启用SimpleBroker,因为它是由代理自动处理的特殊队列

    2) 如果服务器使用例如注解 "@SendToUser("/queue/private")" 客户端必须订阅队列 "/user/queue/private" :你不能在队列中添加用户名因为这是一个由broker处理的透明操作

    我确信这可以正常工作,因为我在我的设置中使用它。

    我没有尝试过 convertAndSendToUser() 方法,但由于它的语义应该与注解相同,它也应该可以工作。

    【讨论】:

    • 很好的回应,非常感谢!我将继续尝试,并尽快更新您/将其标记为已解决。
    • 所以我尝试完全按照您的建议进行操作,但仍然没有成功。我们两种情况之间的关键问题是:您正在使用@SendToUser,它已经具有当前用户会话和范围,并且可能是用户会话。我正在尝试使用convertAndSendToUser() 向其他用户发送通知(可能来自另一个用户),这是我遇到问题的地方
    • 这应该是公认的答案。我知道已经很晚了,但是您可以使用 clientInboundChannel 和 clientOutboundChannel 来停止不应该传入/传出的订阅和消息。
    • 您使用内存代理或远程 JMS 代理来完成这项工作?
    • 嵌入式弹簧代理
    猜你喜欢
    • 2018-11-07
    • 2020-04-16
    • 2012-11-27
    • 2017-12-21
    • 2011-08-12
    • 2016-07-17
    • 1970-01-01
    • 2018-08-09
    相关资源
    最近更新 更多