Vaadin 23首页设匿名访问后,跳转授权页面未重定向至Keycloak登录页
问题描述
原本所有Vaadin视图都使用@PermitAll注解,结合Spring Security + Keycloak配置正常,访问受保护页面会自动跳转到Keycloak登录页。将首页HomeView的注解改为@AnonymousAllowed后,匿名用户可正常访问首页,但点击其他带@PermitAll的视图(如DashboardView)时,系统显示路由找不到错误,而非跳转到Keycloak登录页。
相关关键信息:
- 调试日志显示
SpringViewAccessChecker拒绝访问受保护视图后,直接重定向到RouteNotFoundError页面 - 所有受保护视图仍保留
@PermitAll注解,SecurityConfig中OAuth2登录配置正常
原因分析
问题核心在于Vaadin视图访问检查与Spring Security的执行顺序:
- Vaadin的
SpringViewAccessChecker会优先处理视图上的权限注解(如@PermitAll),当检测到匿名用户访问受保护视图时,默认逻辑是将请求重定向到RouteNotFoundError,而非触发Spring Security的认证跳转流程。 - 此时Spring Security没有机会拦截请求并执行登录重定向,因为Vaadin的路由检查已提前终止了请求处理。
解决方案
以下两种方式均可修复该问题:
方案一:通过Spring Security URL规则直接保护路由
在SecurityConfiguration的configure(HttpSecurity http)方法中添加URL级别的权限规则,让Spring Security优先拦截未认证请求并跳转到登录页:
@Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); // 添加URL权限规则,优先于Vaadin视图检查 http.authorizeRequests() .antMatchers("/").permitAll() // 允许匿名访问首页 .antMatchers("/dashboard", "/inbox", "/jobs", "/candidates", "/administration").authenticated(); // 其他路由需认证 // 保留原有OAuth2和Logout配置 http .oauth2Login(oauth2Login -> oauth2Login .clientRegistrationRepository(clientRegistrationRepository) .userInfoEndpoint(userInfoEndpoint -> userInfoEndpoint .userAuthoritiesMapper(authoritiesMapper) ) .successHandler(new VaadinSavedRequestAwareAuthenticationSuccessHandler()) ) .logout(logout -> logout .logoutSuccessHandler(logoutSuccessHandler()) .logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET")) ); }
此方式下,Spring Security会直接拦截未认证用户访问受保护URL的请求,跳转到Keycloak登录页,绕过Vaadin默认的错误处理逻辑。
方案二:自定义Vaadin访问检查逻辑,触发登录重定向
创建自定义BeforeEnterListener,在视图访问被拒绝时手动触发Spring Security的登录重定向:
@Component public class CustomViewAccessChecker implements BeforeEnterListener { @Override public void beforeEnter(BeforeEnterEvent event) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // 判断用户是否为未认证状态 if (authentication == null || !authentication.isAuthenticated() || authentication instanceof AnonymousAuthenticationToken) { VaadinServletRequest request = VaadinServletRequest.getCurrent(); if (request != null) { try { // 触发Keycloak OAuth2登录重定向 String loginUrl = request.getHttpServletRequest().getContextPath() + "/oauth2/authorization/keycloak"; request.getHttpServletRequest().sendRedirect(loginUrl); event.rerouteTo(HomeView.class); // 终止Vaadin路由处理 } catch (IOException e) { throw new RuntimeException(e); } } } } }
在MainLayout中注册该监听器:
@RouteLayout public class MainLayout extends AppLayout { public MainLayout(CustomViewAccessChecker accessChecker) { addBeforeEnterListener(accessChecker); // 原有createDrawer等初始化代码 } }
此方式会在Vaadin路由检查前判断用户认证状态,未认证则直接触发登录重定向。
注意事项
- 使用方案一时,需确保URL规则与Vaadin路由的
value完全匹配(如DashboardView的@Route(value = "dashboard")对应URL/dashboard) - 两种方案均需保留原有的Spring Security与Keycloak配置,确保登录成功后能跳回原请求页面(依赖
VaadinSavedRequestAwareAuthenticationSuccessHandler)
内容的提问来源于stack exchange,提问作者alexanoid
相关产品推荐
相关产品推荐

