You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vaadin 24按钮监听器路由导航异常:触发登出无报错

Vaadin 24 + Spring Boot 3 路由导航触发登出问题排查与解决

问题现象

将项目从Vaadin 23.3/Spring Boot 2.7.7迁移至Vaadin 24.0/Spring Boot 3后,出现异常路由行为:

  • 直接调用UI.getCurrent().navigate(File_Management.class)可正常跳转
  • 在菜单按钮的点击监听器中调用相同导航代码时,应用自动登出并跳转至登录页,但浏览器URL仍显示目标路由(如https://www.test.com/fileManagement)
  • 切换两个路由的导航目标后,原本正常的路由会触发登出,反之亦然
  • Spring Security及Tomcat日志无报错信息,配置permitAll()后问题仍存在,迁移前版本无此问题

核心代码片段

MainLayout类

@NoArgsConstructor
@Route("")
public class MainLayout extends FlexBoxLayout implements RouterLayout, AfterNavigationObserver { // 原代码拼写错误,应为AfterNavigationObserver

    @PostConstruct
    public void init() {
        UI currentUI = UI.getCurrent();
        
        // 初始化逻辑
        
        // 跳转至主页
        currentUI.access(() -> {
               currentUI.navigate(Main_Dashboard.class);
        });

        menuButton.addClickListener(event -> {
            currentUI.access(() -> {
                   currentUI.navigate(File_Management.class);
            });
        });
    }
}

Main_Dashboard类

@NoArgsConstructor
@Route(value = "mainDashboard", layout = MainLayout.class)
public class Main_Dashboard extends SplitViewFrame {

    @PostConstruct
    public void init() {
           // 初始化逻辑
    }
}

File_Management类

@NoArgsConstructor
@Route(value = "fileManagement", layout = MainLayout.class)
public class File_Management extends SplitViewFrame {

    @PostConstruct
    public void init() {
           // 初始化逻辑
    }
}

问题原因分析

  1. UI实例持有过期:在@PostConstruct中获取的UI实例被长期持有,后续点击事件触发时,该实例可能已脱离当前请求线程,或因Vaadin 24的UI生命周期变化导致失效,进而触发异常安全校验。
  2. CSRF令牌验证失败:Vaadin 24与Spring Boot 3的CSRF整合逻辑发生变化,按钮点击时的导航请求未正确携带Vaadin的CSRF令牌,导致Spring Security判定请求非法并触发登出。
  3. 组件构造方式不规范:使用@NoArgsConstructor而非Spring推荐的构造注入,可能导致组件依赖初始化不完整,影响路由导航的安全上下文传递。
  4. 接口拼写错误:原代码中afterNavigationObserver应为AfterNavigationObserver(首字母大写),未正确实现导航监听接口可能导致路由状态异常。

解决方案

方案1:修正UI实例获取方式

不在@PostConstruct中持有UI实例,而是在点击事件中实时获取当前UI,确保使用当前请求的有效UI实例:

@Route("")
public class MainLayout extends FlexBoxLayout implements RouterLayout, AfterNavigationObserver {

    public MainLayout() {
        // 初始化逻辑
    }

    @PostConstruct
    public void init() {
        // 跳转至主页:直接使用UI.getCurrent(),无需提前持有
        UI.getCurrent().access(() -> {
            UI.getCurrent().navigate(Main_Dashboard.class);
        });

        menuButton.addClickListener(event -> {
            // 点击事件中实时获取当前UI
            UI.getCurrent().access(() -> {
                UI.getCurrent().navigate(File_Management.class);
            });
        });
    }
}

方案2:优化Spring Security配置

确保Spring Security正确兼容Vaadin 24的请求模式,忽略Vaadin内部端点并正确处理CSRF:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth
                        // 允许Vaadin的静态资源和内部端点
                        .requestMatchers("/VAADIN/**", "/favicon.ico", "/robots.txt", "/manifest.webmanifest", "/sw.js", "/offline-page.html")
                        .permitAll()
                        // 所有其他请求需要认证
                        .anyRequest().authenticated()
                )
                // Vaadin 24使用自身的CSRF令牌,禁用Spring Security的CSRF过滤
                .csrf(csrf -> csrf.disable())
                // 配置登录登出逻辑
                .formLogin(form -> form
                        .loginPage("/login")
                        .permitAll()
                )
                .logout(logout -> logout
                        .logoutSuccessUrl("/login")
                        .permitAll()
                );

        return http.build();
    }
}

方案3:规范组件构造方式

移除@NoArgsConstructor,使用构造注入(Spring Boot 3默认推荐构造注入),确保组件依赖正确初始化:

@Route(value = "mainDashboard", layout = MainLayout.class)
public class Main_Dashboard extends SplitViewFrame {

    // 如果有依赖,通过构造注入
    public Main_Dashboard() {
        // 初始化逻辑可直接写在构造函数中,无需@PostConstruct(可选)
    }

    @PostConstruct
    public void init() {
           // 初始化逻辑
    }
}

方案4:修正导航监听接口实现

确保正确实现AfterNavigationObserver接口(首字母大写),避免路由状态异常:

@Route("")
public class MainLayout extends FlexBoxLayout implements RouterLayout, AfterNavigationObserver {

    @Override
    public void afterNavigation(AfterNavigationEvent event) {
        // 可选:处理导航后的逻辑,比如更新菜单选中状态
    }

    // 其他代码
}

验证步骤

  1. 应用上述修改后,重启服务并登录
  2. 点击菜单按钮尝试导航至File_Management,检查是否正常跳转
  3. 查看浏览器控制台及服务器日志,确认无异常信息
  4. 测试不同路由的导航行为,确保问题完全解决

内容的提问来源于stack exchange,提问作者Bemat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 01:17:52