订阅频道时如何检查websocket用户是不是经过身份验证?

Posted

技术标签:

【中文标题】订阅频道时如何检查websocket用户是不是经过身份验证?【英文标题】:How to check if websocket user is authenticated when subscribe a channel?订阅频道时如何检查websocket用户是否经过身份验证? 【发布时间】:2017-12-19 20:36:44 【问题描述】:

我有 websocket 配置类:

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) 
            config.enableSimpleBroker("/topics");
            config.setApplicationDestinationPrefixes("/app");        
    

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) 
        registry.addEndpoint("/notifications").setAllowedOrigins("*").withSockJS();
    

还有ClientInboundChannel:

@Override
public void configureClientInboundChannel(ChannelRegistration registration) 
    registration.setInterceptors(new ChannelInterceptorAdapter() 

        @Override
        public Message<?> preSend(Message<?> message, MessageChannel channel) 

            StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);

            final String xAuthToken = accessor.getFirstNativeHeader(ManipulatesHeaders.X_AUTH_TOKEN_HEADER);

            if (xAuthToken == null) 
                return message;
            

            final UserDetails userDetails = authService.getUserDetails(xAuthToken);

            if (StompCommand.CONNECT == accessor.getCommand()) 
                final WebSocketPrincipal principal = (...)

                userRegistry.registerUser(principal, message);
                accessor.setUser(principal);
            

            return message;
        
    

现在,我想向订阅特定频道的每个用户发送欢迎消息。很明显,可以通过创建一个实现ApplicationListener&lt;SessionSubscribeEvent&gt;并提供的类来实现

@Override
    public void onApplicationEvent(final SessionSubscribeEventevent) 
      StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage());

第二种方法是在前面提到的ClientInboundChannel中执行类似的事情(相同)。

问题是,当用户发送单脚SUBSCRIBE 时,message 中没有 simpUser 标头。

流程如下:用户发送stompCONNECT,执行此行:accessor.setUser(principal),此时message已正确设置simpUser标头。但是当在preSend 中接收到stomp SUBSCRIBE 消息时,StompHeaderAccessor 没有simpUser 标头。因此,我无法评估用户是否已经过身份验证。

那么我如何检查发送SUBSCRIBE 消息的用户是否已经通过身份验证? (以及为什么经过身份验证的用户不发送simpUser 标头)

【问题讨论】:

【参考方案1】:

在您的 websocket 配置类中,将您的扩展更改为 AbstractSecurityWebSocketMessageBrokerConfigurer

那么您可以按如下方式设置您的安全性:

@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry message) 
    message
      .nullDestMatcher().permitAll()
      .simpDestMatchers("/app/**").authenticated()
      .simpSubscribeDestMatchers("/topics/**").authenticated()
      .anyMessage().denyAll();

如果您收到错误 Could not verify the provided CSRF token because your session was not found,则必须在 websocket 配置类中覆盖 `sameOriginDisabled

  @Override
  protected boolean sameOriginDisabled() 
      return true;
  

有关 websocket 安全性的更多信息:

https://docs.spring.io/spring-security/site/docs/current/reference/html/websocket.html

【讨论】:

所以基本上这个想法是,.authenticated() 我不必处理任何“通过 stomp 身份验证的令牌”,因为它是由 Spring Security 完成的。基本上我可以为特定频道订阅创建onApplicationEvent(final SessionSubscribeEvent event),因为我知道如果用户能够到达这里,则必须经过身份验证。我说的对吗? 没错。如果您通过某种类型的身份验证提供程序(通常是UsernamePasswordAuthenticationToken)设置安全原则,spring security 将进行检查,并且不允许未经身份验证的订阅者发送/接收消息。 我确实使用UsernamePasswordAuthenticationToken。我不断收到message:Failed to send message to ExecutorSubscribableChannel[clientInboundChannel]; nested exception is org.springframework.security.web.csrf.MissingCsrfTokenException\c Could not verify the provided CSRF token because your session was not found. 任何想法可能是什么原因或我该如何调试? 既然你 setAllowedOrigin("*") 你将不得不覆盖 sameOriginDisabled (见我修改后的答案)

以上是关于订阅频道时如何检查websocket用户是不是经过身份验证?的主要内容,如果未能解决你的问题,请参考以下文章

YouTube API。检查用户是不是已订阅

如何确定用户是不是订阅了 youtube 频道

订阅 Action Cable 频道时如何设置参数

如何检查我是不是已经订阅了 parse.com 推送通知

Javascript Websocket订阅频道

EventMachine WebSockets - 订阅 WS 到 EM 频道与保持套接字收集