Решение для: - CometD 4.0.2 - Spring 4.3.9.RELEASE - Spring Security 4.1.0.RELEASE
В моем проекте мы столкнулись с аналогичной проблемой при переходе от «длинного пула» к «веб-сокету»подключение.Для пояснения - одно отличие - мы использовали «пользовательский контекст» только во время «рукопожатия».
При использовании «длинного пула» Cookie с JSESSIONID правильно интерпретировался Spring, и мы работали в «пользовательском контексте».После переключения на «websocket» это выглядело так, как будто «пользовательский контекст» был потерян непосредственно перед запуском кода получения сообщений.
Мы не смогли найти правильное решение - следующий код, на мой взгляд, просто хак.Но это сработало для нас.
import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.ServerMessage;
import org.cometd.bayeux.server.ServerSession;
import org.cometd.server.DefaultSecurityPolicy;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
public class BayeuxAuthenticator extends DefaultSecurityPolicy implements ServerSession.RemoveListener {
// (...)
@Override
public boolean canHandshake( BayeuxServer server, ServerSession session, ServerMessage message ) {
SecurityContextHolder.getContext().setAuthentication( ( UsernamePasswordAuthenticationToken ) message.getBayeuxContext().getUserPrincipal() );
String user = securityService.getUserName();
// (...)
return true;
}
// (...)
}
Где следующая строка является наиболее важной
SecurityContextHolder.getContext().setAuthentication( ( UsernamePasswordAuthenticationToken ) message.getBayeuxContext().getUserPrincipal() );
Редактировать 2:
Мы получаем данныеиз
String user = securityService.getUserName();
, где securityService - это Spring @Service, защищенный Spring @Secured (/ * ожидаемые роли * /).(Аннотация @Secured создала нам исключения авторизации перед настройкой контекста)
Внутри есть чтение контекста и пользовательские данные:
SecurityContextHolder.getContext().getUserPrincipal().getName()
Но, честно говоря, если вам просто нужны "принципалы пользователя"может быть достаточно прочитать его из:
message.getBayeuxContext().getUserPrincipal() // instanceof java.security.Principal
Который в нашем случае был типа
org.springframework.security.authentication.UsernamePasswordAuthenticationToken
Может быть, в вашем случае это другой подкласс?
Дляразъяснение - во время запуска соединения через веб-сокет мы передали Cookie с правильным JSESSIONID в запросе.
Запрос на соединение WebSocket:
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,pl;q=0.8
Cache-Control: no-cache
Connection: Upgrade
Cookie: XSRF-TOKEN=<some token>; Idea-<some token>; lastOpenTab=sourcing/content; io=<some token>; BAYEUX_BROWSER=<some token>; JSESSIONID=<some token>
Host: localhost:8090
Origin: http://localhost:8090
Pragma: no-cache
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key: NTtWtXAL8ReIB0LjvI2G0g==
Sec-WebSocket-Version: 13
Upgrade: websocket
User-Agent: Mozilla/5.0 ...