Я помню, что наткнулся на ту же проблему в проекте, над которым я работал. Поскольку я не мог найти решение, используя документацию Spring - и другие ответы по переполнению стека не работали для меня - я в итоге создал обходной путь.
Хитрость заключается в том, чтобы заставить приложение аутентифицировать пользователя по запросу соединения WebSocket. Для этого вам нужен класс, который перехватывает такие события, а затем, как только вы получите контроль над этим, вы можете вызвать свою логику аутентификации.
Создайте класс, который реализует ChannelInterceptorAdapter
Spring. Внутри этого класса вы можете внедрить любые bean-компоненты, которые вам необходимы для фактической аутентификации Мой пример использует базовую аутентификацию:
@Component
public class WebSocketAuthInterceptorAdapter extends ChannelInterceptorAdapter {
@Autowired
private DaoAuthenticationProvider userAuthenticationProvider;
@Override
public Message<?> preSend(final Message<?> message, final MessageChannel channel) throws AuthenticationException {
final StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
StompCommand cmd = accessor.getCommand();
if (StompCommand.CONNECT == cmd || StompCommand.SEND == cmd) {
Authentication authenticatedUser = null;
String authorization = accessor.getFirstNativeHeader("Authorization:);
String credentialsToDecode = authorization.split("\\s")[1];
String credentialsDecoded = StringUtils.newStringUtf8(Base64.decodeBase64(credentialsToDecode));
String[] credentialsDecodedSplit = credentialsDecoded.split(":");
final String username = credentialsDecodedSplit[0];
final String password = credentialsDecodedSplit[1];
authenticatedUser = userAuthenticationProvider.authenticate(new UsernamePasswordAuthenticationToken(username, password));
if (authenticatedUser == null) {
throw new AccessDeniedException();
}
SecurityContextHolder.getContext().setAuthentication(authenticatedUser);
accessor.setUser(authenticatedUser);
}
return message;
}
Затем в вашем классе WebSocketConfig
вам необходимо зарегистрировать ваш перехватчик. Добавьте приведенный выше класс в качестве компонента и зарегистрируйте его. После этих изменений ваш класс будет выглядеть так:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Autowired
private WebSocketAuthInterceptorAdapter authInterceptorAdapter;
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/app").withSockJS();
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.setInterceptors(authInterceptorAdapter);
super.configureClientInboundChannel(registration);
}
}
Очевидно, детали логики аутентификации зависят от вас. Вы можете позвонить в службу JWT или использовать то, что вы используете.