订阅频道时如何检查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<SessionSubscribeEvent>
并提供的类来实现
@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用户是不是经过身份验证?的主要内容,如果未能解决你的问题,请参考以下文章