Vaadin 14.2:使用 BeforeLeaveListener 拦截注销(使会话无效)
Vaadin 14.2: Intercept logout (invalidate session) with 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);
除注销外,这对所有情况都适用。
这是我的注销按钮在开始时的样子(只要我不想 postpone/cancel 注销,它就可以工作):
Anchor link = new Anchor();
link.getElement().addEventListener("click", e -> {
VaadinSession.getCurrent().getSession().invalidate();
UI.getCurrent().navigate(LoginView.class);
});
现在我想推迟注销,直到用户确认应丢弃未保存的更改。 (在 EventListener 中切换两行似乎是个好主意,但由于以下原因没有奏效。)
在 navigate-call 中调用了 BeforeLeaveListener(好),但是注销还是完成了,因为执行 navigate()-call 之后的代码(当然,因为它不是阻塞调用),会话无效并且虽然刚刚弹出确认对话框,但用户已注销(但用户没有机会confirm/deny)。
我试图将会话失效移动到 LoginView 中(移动到 BeforeEnterObserver 中),但结果是登录视图在无限循环中重新加载。
问题:有没有类似"navigate to LoginView and if navigation is not postponed and not cancelled, then invalidate session"的东西?
一个解决方法是导航到一个截取的 LogoutView,它只是使会话无效,redirects/forwards 到 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 安全部门正在做的事情。
我的注销按钮(使用 Spring 安全)看起来像这样:
new Button("Logout", click -> {
UI.getCurrent().getPage().executeJs("location.assign('logout')");
});
单击它后,您将注销并重定向到登录视图 (more details from Baeldung)。我认为如果您执行 LogoutView 重定向绕行就没问题了。
您甚至可以使用 Navigator 转到您的 LogoutView 的事实在这里更好,因为这会被 BeforeLeaveObserver 捕获(与通过分配新位置或刷新页面的全新请求形成对比。参见 BeforeLeaveObserver vs. beforeunload 了解更多详情)
我仍然想为您的用例提出另一个想法。我认为它会简单得多,但要求注销 link 知道 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);
}
在我的 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);
除注销外,这对所有情况都适用。 这是我的注销按钮在开始时的样子(只要我不想 postpone/cancel 注销,它就可以工作):
Anchor link = new Anchor();
link.getElement().addEventListener("click", e -> {
VaadinSession.getCurrent().getSession().invalidate();
UI.getCurrent().navigate(LoginView.class);
});
现在我想推迟注销,直到用户确认应丢弃未保存的更改。 (在 EventListener 中切换两行似乎是个好主意,但由于以下原因没有奏效。) 在 navigate-call 中调用了 BeforeLeaveListener(好),但是注销还是完成了,因为执行 navigate()-call 之后的代码(当然,因为它不是阻塞调用),会话无效并且虽然刚刚弹出确认对话框,但用户已注销(但用户没有机会confirm/deny)。
我试图将会话失效移动到 LoginView 中(移动到 BeforeEnterObserver 中),但结果是登录视图在无限循环中重新加载。
问题:有没有类似"navigate to LoginView and if navigation is not postponed and not cancelled, then invalidate session"的东西?
一个解决方法是导航到一个截取的 LogoutView,它只是使会话无效,redirects/forwards 到 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 安全部门正在做的事情。
我的注销按钮(使用 Spring 安全)看起来像这样:
new Button("Logout", click -> {
UI.getCurrent().getPage().executeJs("location.assign('logout')");
});
单击它后,您将注销并重定向到登录视图 (more details from Baeldung)。我认为如果您执行 LogoutView 重定向绕行就没问题了。
您甚至可以使用 Navigator 转到您的 LogoutView 的事实在这里更好,因为这会被 BeforeLeaveObserver 捕获(与通过分配新位置或刷新页面的全新请求形成对比。参见 BeforeLeaveObserver vs. beforeunload 了解更多详情)
我仍然想为您的用例提出另一个想法。我认为它会简单得多,但要求注销 link 知道 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);
}