Добрый день.
В итоге получилось следующее:
public class WebsocketTicketAuthenticationRequestHandler extends SynchronizedRequestHandler implements RequestHandler {
protected final Logger logger = LoggerFactory.getLogger(getClass());
public static final String PARAM_TICKET = "ticket";
public static final String PARAM_LOCALE = "locale";
protected final ApplicationContext applicationContext;
protected final WSAuthenticationBean wsAuthenticationBean;
public WebsocketTicketAuthenticationRequestHandler(ApplicationContext applicationContext) {
super();
this.applicationContext = applicationContext;
this.wsAuthenticationBean = this.applicationContext.getBean(WSAuthenticationBean.class);
}
@Override
public boolean synchronizedHandleRequest(VaadinSession session, VaadinRequest request, VaadinResponse response) throws IOException {
logger.debug("synchronizedHandleRequest : session -> {}, request -> {}, response -> {}"
, session
, request
, response);
try {
if (!request.getPathInfo().startsWith(RouteUtil.getRoutePath(request.getService().getContext()
, FramesMainView.class))
|| request.getPathInfo().startsWith(RouteUtil.getRoutePath(request.getService().getContext()
, FramesConnectionFailedView.class))
|| request.getPathInfo().startsWith(RouteUtil.getRoutePath(request.getService().getContext()
, FramesConnectionClosedView.class))) {
return false;
}
Map<String, String[]> requestParameterMap = request.getParameterMap();
String[] ticketStringArray = requestParameterMap.get(PARAM_TICKET);
if (Objects.isNull(ticketStringArray)
|| ticketStringArray.length < 1) {
return false;
}
Locale locale = null;
try {
locale = LocaleResolver.resolve(requestParameterMap.get(PARAM_LOCALE)[0]);
} catch (Exception e) {
//ничего не делаем
}
//тикет
String ticketString = ticketStringArray[0];
UUID ticket = UuidProvider.fromString(ticketString);
//нежно "входим"
Authentication authentication = wsAuthenticationBean.authenticate(ticket
, locale);
//предполагается аноним
if (Objects.isNull(authentication)) {
session.getSession().removeAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
return false;
}
SecurityContextHelper.setAuthentication(authentication);
session.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY
, SecurityContextHolder.getContext());
return false;
} catch (Exception e) {
logger.error(e.getMessage()
, e);
redirectOnException(response);
return true;
}
}
protected void redirectOnException(VaadinResponse response) {
logger.debug("forwardOnException : response -> {}"
, response);
response.setNoCacheHeaders();
response.setStatus(HttpStatus.TEMPORARY_REDIRECT.value());
response.setHeader(HttpHeaders.LOCATION
, RouteUtil.getRoutePath(response.getService().getContext()
, FramesConnectionFailedView.class));
}
@Component(WSAuthenticationBean.NAME)
public class WSAuthenticationBean {
public static final String NAME = "ws_WSAuthenticationBean";
protected final Logger logger = LoggerFactory.getLogger(getClass());
protected final ApplicationContext applicationContext;
@Autowired
protected AuthenticationManager authenticationManager;
@Autowired
protected CurrentAuthentication currentAuthentication;
@Autowired
protected SystemAuthenticator systemAuthenticator;
@Autowired
protected UserRepository userRepository;
@Autowired
protected CoreProperties coreProperties;
@Autowired
protected UiProperties uiProperties;
@Autowired
protected SessionHolder sessionHolder;
@Autowired
protected SessionAuthenticationStrategy authenticationStrategy;
@Autowired
protected SecurityContextRepository securityContextRepository;
@Autowired
protected ApplicationEventPublisher applicationEventPublisher;
@Autowired
protected WSTicketService ticketService;
@Autowired
protected UserService userService;
public WSAuthenticationBean(ApplicationContext applicationContext) {
super();
logger.info("{} : power on"
, getClass().getSimpleName());
this.applicationContext = applicationContext;
}
public Authentication authenticate(UUID ticket
, @Nullable Locale locale) {
logger.debug("authorize : ticket -> {}"
, ticket);
//тикета нет
if (Objects.isNull(ticket)) {
logger.warn("authorize : ticket is null");
throw new WSTicketBadException();
}
//тикет невалиден или не подключен
if (!ticketService.check(ticket)
|| !ticketService.isWebSocketConnected(ticket)) {
logger.warn("authorize : ticket not valid/connected");
throw new WSTicketNotConnectedException();
}
//мы в той же сессии
if (ticketService.checkWebClientSessionId(ticket
, getCurrentVaadinSessionId())) {
logger.debug("authorize : ticket check success");
return SecurityContextHelper.getAuthentication();
}
String ticketUsername = ticketService.getUsername(ticket);
String ticketSessionId = ticketService.getWebClientSessionId(ticket);
List<VaadinSession> vaadinSessions = sessionHolder.getActiveSessionsForUsernames(List.of(ticketUsername)).getOrDefault(ticketUsername
, new ArrayList<>());
vaadinSessions.stream()
.filter(vaadinSession -> Objects.equals(vaadinSession.getSession().getId()
, ticketSessionId)
&& !Objects.equals(vaadinSession.getSession().getId()
, getCurrentVaadinSessionId()))
.forEach(vaadinSession -> {
try {
systemAuthenticator.runWithUser(ticketUsername
, () -> vaadinSession.access(() -> onSessionAccess(vaadinSession)));
} catch (Exception e) {
String message = String.format("authorize : can not invalidate active session for username [%s] and ticket [%s] with error [%s] -> [%s]"
, ticketUsername
, ticket
, e.getClass()
, e.getMessage());
logger.error(message);
}
});
Authentication authentication = null;
if (Objects.equals(ticketUsername
, currentAuthentication.getUser().getUsername())) {
authentication = SecurityContextHelper.getAuthentication();
}
if (!ticketService.isForAnonymous(ticket)
&& Objects.isNull(authentication)) {
VaadinServletRequest request = VaadinServletRequest.getCurrent();
SystemAuthenticationToken systemAuthenticationToken = new SystemAuthenticationToken(ticketUsername);
TimeZone timeZone = TimeZone.getDefault();
try {
timeZone = TimeZone.getTimeZone(((HasTimeZone) userRepository.loadUserByUsername(ticketUsername)).getTimeZoneId());
} catch (Exception e) {
//ничего не делаем
}
ClientDetails clientDetails = ClientDetails.builder()
.locale(Objects.nonNull(locale)
? locale
: getDefaultLocale())
.scope(SecurityScope.UI)
.sessionId(request.getSession().getId())
.timeZone(timeZone)
.build();
systemAuthenticationToken.setDetails(clientDetails);
authentication = authenticationManager.authenticate(systemAuthenticationToken);
VaadinSession vaadinSession = VaadinSession.getCurrent();
try {
vaadinSession.lock();
preventSessionFixation(authentication);
onSuccessfulAuthentication(authentication
, systemAuthenticationToken);
} catch (Exception e) {
String message = String.format("authenticate : error on prevent session fixation issue -> %s"
, e.getMessage());
logger.error(message
, e);
} finally {
try {
vaadinSession.unlock();
} catch (Exception e) {
logger.warn("authenticate : error with vaadin session unlock -> {}"
, e.getMessage());
}
}
}
SecurityContextHelper.setAuthentication(authentication);
ticketService.setWebClientSessionId(ticket
, getCurrentVaadinSessionId());
return authentication;
}
protected String getCurrentVaadinSessionId() {
try {
return VaadinRequest.getCurrent().getWrappedSession().getId();
} catch (Exception e) {
logger.warn("getCurrentVaadinSessionId : no session id -> {}"
, e.getMessage());
return null;
}
}
protected void onSessionAccess(VaadinSession session) {
logger.debug("onSessionAccess : session -> {}"
, session);
//перенаправляем все открытые интерфейсы на экран отображающий закрытие сессии
session.getUIs().forEach(ui -> ui.access(() -> onUIAccess(ui)));
session.getSession().removeAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
}
protected void onUIAccess(UI ui) {
logger.debug("onUIAccess : ui -> {}"
, ui);
if (ui.isClosing()) {
return;
}
ui.navigate(FramesConnectionClosedView.class);
}
protected Locale getDefaultLocale() {
List<Locale> locales = coreProperties.getAvailableLocales();
return locales.get(0);
}
protected void preventSessionFixation(Authentication authentication) {
if (authentication.isAuthenticated()
&& Objects.nonNull(VaadinRequest.getCurrent())
&& uiProperties.isUseSessionFixationProtection()) {
VaadinService.reinitializeSession(VaadinRequest.getCurrent());
}
}
protected void onSuccessfulAuthentication(Authentication authentication,
SystemAuthenticationToken systemAuthenticationToken) {
VaadinServletRequest request = VaadinServletRequest.getCurrent();
VaadinServletResponse response = VaadinServletResponse.getCurrent();
request.setAttribute(DEFAULT_PARAMETER
, false);
if (authenticationStrategy != null) {
authenticationStrategy.onAuthentication(authentication
, request
, response);
}
SecurityContextHelper.setAuthentication(authentication);
securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response);
applicationEventPublisher.publishEvent(
new InteractiveAuthenticationSuccessEvent(authentication
, this.getClass()));
}
}
, где:
- FramesMainView - дополнительный StandardMainView без каких либо компонентов;
- FramesConnectionFailedView, FramesConnectionClosedView - экраны с layout=FramesMainView.class
Спасибо.