【问题标题】: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 标头)

【问题讨论】:

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


    【解决方案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 (见我修改后的答案)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-09
    • 1970-01-01
    • 2013-04-30
    相关资源
    最近更新 更多