【问题标题】:Vaadin 14.2: Intercept logout (invalidate session) with BeforeLeaveListenerVaadin 14.2:使用 BeforeLeaveListener 拦截注销(使会话无效)
【发布时间】:2020-06-04 13:52:48
【问题描述】:

在我的 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(很好),但是注销仍然完成,因为执行了导航()调用之后的代码(当然,因为它不是阻塞调用),会话无效并且虽然刚刚弹出确认对话框,但用户已注销(但用户没有机会确认/拒绝)。

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

问题:是否有类似“导航到 LoginView 并且如果导航没有被推迟也没有取消,则使会话无效”之类的东西?

一种解决方法是导航到一个拦截 LogoutView,它只会使会话无效并重定向/转发到 LoginView:

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

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

【问题讨论】:

    标签: vaadin vaadin-flow vaadin10


    【解决方案1】:

    我知道,你没有提到 Spring。但这实际上是我认为 Spring Security 在幕后所做的。

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

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

    单击它后,您将注销并重定向到登录视图 (more details from Baeldung)。我认为如果您执行 LogoutView 重定向绕道就可以了。

    您甚至可以使用 Navigator 转到 LogoutView 的事实在这里更好,因为这是由 BeforeLeaveObserver 捕获的(与通过分配新位置或刷新页面的全新请求形成对比。请参阅BeforeLeaveObserver vs. beforeunload for更多细节)


    我仍然想为您的用例提出另一个想法。我认为它会简单得多,但要求注销链接知道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/listener。哇,这太神奇了(......或者可能是我的 WebSecurityConfigurerAdapter 实现中的方法 configure(HttpSecurity) 中的 logout() 调用,它是默认 Vaadin 项目附带的......)。谢谢!
    • ...or maybe the logout()-call in method configure(HttpSecurity) in my WebSecurityConfigurerAdapter implementation - 完全正确。请注意如果您使用location.assign('logout') 执行此操作,BeforeLeaveObserver 不会捕获它,而是使用beforeunload(请参阅答案中的链接)`
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-24
    • 1970-01-01
    • 1970-01-01
    • 2011-10-09
    • 2015-08-14
    • 2012-01-21
    • 2021-03-09
    相关资源
    最近更新 更多