Spring Boot多租户集成Keycloak登录后陷入无限重定向循环
Spring Boot多租户应用Keycloak登录后无限重定向问题
问题现象
Spring Boot多租户集成Keycloak认证时,Keycloak端登录验证通过,但重定向回应用后陷入无限重定向循环。调试确认访问Keycloak时使用的租户与Realm均正确,浏览器网络日志显示持续重复重定向操作。
相关代码配置
SecurityConfig 安全配置
@Configuration @EnableWebSecurity class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http, ClientRegistrationRepository clients) throws Exception { http. authorizeHttpRequests(auth -> auth .requestMatchers("/", "/webjars/**", "/js/**", "/images/**", "/actuator/**", "/login").permitAll() .anyRequest().authenticated() ) .oauth2Login(oath2 -> oath2 .clientRegistrationRepository(clients) .loginPage("/login") .defaultSuccessUrl("/home", false) ) .logout(logout -> logout .logoutSuccessHandler(tenantLogoutHandler()) .logoutSuccessUrl("/") .invalidateHttpSession(true) .clearAuthentication(true) .deleteCookies("JSESSIONID") ); return http.build(); } @Bean LogoutSuccessHandler tenantLogoutHandler() { return ((request, response, authentication) -> { String t = TenantContext.getTenant(); String logoutUrl = String.format( "http://localhost:8081/realms/%s/protocol/openid-connect/logout?redirect_uri=http://%s:8080/", t, t + ".localhost" ); response.sendRedirect(logoutUrl); }); } }
LoginController 登录控制器
@Controller class LoginController { @GetMapping("/login") public String login() { String tenant = TenantContext.getTenant(); return "redirect:/oauth2/authorization/" + tenant; } }
问题排查与解决方案
1. 放行OAuth2回调端点
当前安全配置未放行OAuth2回调路径/login/oauth2/code/**,导致回调请求被认证过滤器拦截,触发重定向到/login,形成循环。需将该路径加入permitAll列表:
.authorizeHttpRequests(auth -> auth .requestMatchers("/", "/webjars/**", "/js/**", "/images/**", "/actuator/**", "/login", "/login/oauth2/code/**").permitAll() .anyRequest().authenticated() )
2. 确保租户上下文在回调阶段可用
重定向回调时,TenantContext可能未正确设置,导致LoginController重复触发授权跳转。需在请求进入Spring Security前解析租户并设置上下文,例如添加租户解析过滤器:
@Component public class TenantFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; String host = req.getServerName(); // 从域名提取租户标识,如`tenant.localhost`提取`tenant` String tenant = host.split("\\.")[0]; TenantContext.setTenant(tenant); try { chain.doFilter(request, response); } finally { // 请求结束后清理上下文,避免线程污染 TenantContext.clearTenant(); } } }
并确保该过滤器在Spring Security过滤器链之前执行。
3. 验证defaultSuccessUrl配置
确认defaultSuccessUrl("/home", false)的参数逻辑:第二个参数设为false表示仅在登录成功后首次跳转时使用该URL,若用户直接访问受保护资源,登录后会跳转到原资源路径。若业务需要强制跳转首页,可改为true,但此配置并非当前循环的核心原因。
内容的提问来源于stack exchange,提问作者Zouffke
相关产品推荐
相关产品推荐

