Vaadin 14.2:使用BeforeLeaveListener拦截注销(使会话无效)

Vaadin 14.2:使用BeforeLeaveListener拦截注销(使会话无效),vaadin,vaadin-flow,vaadin10,Vaadin,Vaadin Flow,Vaadin10,在my Vaadin 14.2.0应用程序中,有一个BeforeLeaveListener,用于在输入脏时显示确认对话框(=输入字段中有未保存的更改)以取消(或有意继续)导航: BeforeLeaveListener listener = new BeforeLeaveListener() { @Override public void beforeLeave(BeforeLeaveEvent event) { if (dirtyFlag.isDirty()) {

在my 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);
除了注销之外,这一切都很好。 这是我的注销按钮在开始时的样子(只要我不想推迟/取消注销,它就可以工作):

现在我想推迟注销,直到用户确认应该放弃未保存的更改。(在EventListener中切换这两行似乎是一个好主意,但由于以下原因而不起作用。) 在navigate调用中,BeforeLeaveListener被调用(好),但是注销仍然完成,因为navigate()调用之后的代码被执行(当然,因为它不是阻塞调用),会话无效,用户通过刚刚弹出的确认对话框注销(但用户没有机会确认/拒绝)

我试图将会话失效转移到LoginView(一个BeforeEnterObserver)中,但结果是登录视图被无限循环地重新加载

问:是否有类似“导航到LoginView,如果导航未被推迟或取消,则使会话无效”的内容

解决方法是导航到拦截日志out视图,该视图仅使会话无效并重定向/转发到LoginView:

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

但在我看来,这只是一个很糟糕的解决方法,有一些开销(创建一个视图只是为了转发到另一个视图).

我知道,您没有提到Spring。但事实上,我相信Spring Security在幕后就是这么做的

我的注销按钮(使用Spring Security)如下所示:

new Button("Logout", click -> {
    UI.getCurrent().getPage().executeJs("location.assign('logout')");
});
单击后,您将注销并重定向到登录视图()。我想如果你绕道注销视图重定向就可以了

事实上,您甚至可以使用Navigator转到您的LogoutView,这在这里就更好了,因为BeforeLeaveObserver会捕捉到这一点(与通过分配新位置或刷新页面而发出的全新请求相反。有关更多详细信息,请参阅)


我仍然想为您的用例提出另一个想法。我认为这会简单得多,但需要注销链接知道
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);
}

是的,我使用Spring Security。但我不知道它在“/logout”注册了一个servlet/侦听器。哇,这太神奇了(…或者可能是默认Vaadin项目附带的WebSecurityConfigureAdapter实现中的logout()-call-in方法configure(HttpSecurity))。谢谢大家!<代码>…或者可能是我的WebSecurity配置适配器实现中的logout()-调用方法configure(HttpSecurity)-完全正确。请注意,如果您使用
location.assign('logout')
,BeforeLeaveObserver将不会捕获它,而是使用
beforeunload
(请参阅答案中的链接)`
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);
}