Vaadin 14.2: перехватить выход из системы (сделать сеанс недействительным) с помощью BeforeLeaveListener

в моем приложении Vaadin 14.2.0 есть BeforeLeaveListener для отображения диалогового окна подтверждения, когда ввод грязный (= есть несохраненные изменения в полях ввода), чтобы отменить (или намеренно продолжить) навигацию:

BeforeLeaveListener listener = new BeforeLeaveListener() {
    @Override
    public void beforeLeave(BeforeLeaveEvent event) {
        if (dirtyFlag.isDirty()) {
            ContinueNavigationAction postponeAction = event.postpone();
            // show confirmation dialog and maybe execute a proceed
            [...] () -> postponeAction.proceed();
        }
    }
};
UI.getCurrent().addBeforeLeaveListener(listener);

Это отлично работает для всего, кроме выхода из системы. Вот так моя кнопка выхода из системы выглядела в начале (что работает, пока я не хочу откладывать / отменять выход):

Anchor link = new Anchor();
link.getElement().addEventListener("click", e -> {
    VaadinSession.getCurrent().getSession().invalidate();
    UI.getCurrent().navigate(LoginView.class);
});

Теперь я хочу отложить выход из системы до тех пор, пока пользователь не подтвердит, что несохраненные изменения должны быть отменены. (Переключение обеих строк в EventListener кажется хорошей идеей, но не сработало из-за следующего.) Внутри вызова навигации вызывается BeforeLeaveListener (хорошо), но, тем не менее, выход из системы выполняется, потому что код после перехода () -call выполняется (конечно, потому что это не блокирующий вызов), сеанс аннулируется, и пользователь выходит из системы, хотя диалоговое окно подтверждения только что появилось (но у пользователя не было возможности подтвердить / отклонить).

Я попытался переместить аннулирование сеанса в LoginView (в BeforeEnterObserver), но в результате представление входа в систему перезагружалось в бесконечном цикле.

Вопрос: есть ли что-то вроде «перейдите в LoginView, и если навигация не откладывается и не отменяется, то аннулировать сеанс»?

Обходной путь - перейти к перехватывающему Log out View, который просто аннулирует сеанс и перенаправляет / пересылает на LoginView:

@Route(value = "logout")
public class LogoutView implements BeforeEnterObserver {
    @Override
    public void beforeEnter(BeforeEnterEvent event) {
        VaadinSession.getCurrent().getSession().invalidate();
        event.forwardTo(LoginView.class);
    }
}

Но мне кажется, что это просто плохой обходной путь с некоторыми накладными расходами (создание представления только для перехода к другому представлению) ...


person S. Doe    schedule 04.06.2020    source источник


Ответы (1)


Я знаю, вы не упомянули весну. Но на самом деле это то, что, как я считаю, Spring Security делает под капотом.

Моя кнопка выхода (с использованием Spring Security) выглядит так:

new Button("Logout", click -> {
    UI.getCurrent().getPage().executeJs("location.assign('logout')");
});

Как только вы нажмете на нее, вы выйдете из системы и будете перенаправлены к представлению входа в систему (дополнительная информация от Baeldung ). Я думаю, будет хорошо, если вы сделаете обходной путь перенаправления LogoutView.

Тот факт, что вы даже можете использовать навигатор для перехода в LogoutView, здесь даже лучше, поскольку он перехватывается BeforeLeaveObserver (в отличие от совершенно новых запросов путем назначения нового местоположения или обновления страницы. См. BeforeLeaveObserver vs. beforeunload, чтобы узнать об этом подробнее)


Я все же хотел бы предложить другую идею для вашего варианта использования. Я думаю, это будет намного проще, но для этого необходимо, чтобы ссылка для выхода знала dirtyFlag:

Anchor link = new Anchor();
link.getElement().addEventListener("click", e -> {
    if(dirtyFlag.isDirty()){
        // show Dialog with 
        // - a confirm btn that calls doLogout() on click
        // - a cancel btn that closes the Dialog
    } else {
       doLogout();
    }
});

...

private void doLogout(){
    VaadinSession.getCurrent().getSession().invalidate();
    UI.getCurrent().navigate(LoginView.class);
}
person kscherrer    schedule 04.06.2020
comment
Да, я использую Spring Security. Но я не знал, что он регистрирует сервлет / слушателя при выходе из системы. Вау, это волшебство (... или, может быть, вызов метода logout () в методе configure (HttpSecurity) в моей реализации WebSecurityConfigurerAdapter, которая идет с проектом Vaadin по умолчанию ...). Спасибо! - person S. Doe; 04.06.2020
comment
...or maybe the logout()-call in method configure(HttpSecurity) in my WebSecurityConfigurerAdapter implementation - точно. Имейте в виду, что если вы сделаете это с помощью location.assign('logout'), это будет перехвачено не BeforeLeaveObserver, а beforeunload (см. Ссылку в ответе) ` - person kscherrer; 05.06.2020