Spring Security登出重定向外部认证服务器时的CORS问题
我来帮你解决这个Azure B2C登出的CORS问题,核心原因是你当前的配置让前端AJAX请求直接去调用Azure的logout端点,而这个端点是设计给浏览器导航用的,不支持带自定义头(比如你的X-XSRF-TOKEN)的跨域AJAX请求。下面是纯Spring端的解决方案:
问题根源分析
你的前端用fetch发起登出请求后,Spring配置的logoutSuccessUrl会让前端收到302重定向,随后fetch会自动跟进去请求Azure的logout端点——这时候是跨域AJAX请求,还带了X-XSRF-TOKEN头。Azure B2C的logout端点虽然通过了预检请求,但实际响应没有返回Access-Control-Allow-Origin头,导致浏览器触发CORS报错。
纯Spring端解决方案
我们需要修改Spring Security的登出逻辑:先完成Spring端的会话清除,然后让浏览器直接导航到Azure的logout端点(而非让AJAX请求去调用),这样就不会触发CORS检查。
1. 自定义登出成功处理器
创建一个LogoutSuccessHandler的实现类,负责清除本地会话、构造Azure的登出URL,然后返回让浏览器跳转的响应:
import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class AzureB2CLogoutSuccessHandler implements LogoutSuccessHandler { private final String tenant; private final String policy; private final String postLogoutRedirectUri; // 构造函数注入配置参数 public AzureB2CLogoutSuccessHandler(String tenant, String policy, String postLogoutRedirectUri) { this.tenant = tenant; this.policy = policy; this.postLogoutRedirectUri = postLogoutRedirectUri; } @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { // 1. 清除Spring端的会话和Security上下文 request.getSession().invalidate(); // 2. 构造Azure B2C的登出URL(替换成你的实际参数) String azureLogoutUrl = String.format( "https://%s.b2clogin.com/%s.onmicrosoft.com/%s/oauth2/v2.0/logout?post_logout_redirect_uri=%s", tenant, tenant, policy, postLogoutRedirectUri ); // 3. 返回HTML响应,用JS强制浏览器跳转(兼容AJAX请求场景) response.setContentType("text/html;charset=UTF-8"); response.getWriter().write(String.format(""" <!DOCTYPE html> <html> <body> <script> window.location.href = "%s"; </script> Logging out... If not redirected automatically, <a href="%s">click here</a>. </body> </html> """, azureLogoutUrl, azureLogoutUrl)); } }
2. 修改Spring Security配置
替换原来的logoutSuccessUrl配置,改用自定义的处理器:
@Override protected void configure(HttpSecurity http) throws Exception { OidcUserService oidcUserService = new OidcUserService(); // 初始化自定义登出处理器,替换为你的实际租户、策略和URL编码后的重定向URI AzureB2CLogoutSuccessHandler logoutHandler = new AzureB2CLogoutSuccessHandler( "your-tenant-id", "your-signup-signin-policy", "https%3A%2F%2Fjwt.ms%2F" ); http .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .and() .requestMatchers() .antMatchers("/camunda/**", "/api/**", "/login/**", "/oauth2/**") .and() .authorizeRequests() .anyRequest().authenticated() .and() .oauth2Login() .loginPage("/oauth2/authorization/azure-ac") .userInfoEndpoint().oidcUserService(oidcUserService) .and() .logout() .logoutUrl("/camunda/api/admin/auth/user/default/logout") .logoutSuccessHandler(logoutHandler) // 使用自定义处理器替代logoutSuccessUrl .invalidateHttpSession(true) .deleteCookies("JSESSIONID", "XSRF-TOKEN"); // 清除相关Cookie }
关键说明
- 为什么这样能解决CORS问题?因为我们不再让前端AJAX直接请求Azure的端点,而是让浏览器通过JS执行页面导航——这是正常的浏览器跳转行为,不会触发跨域AJAX的CORS检查。
- 请确保你的
post_logout_redirect_uri已经在Azure B2C的应用注册中添加为允许的重定向URI,否则Azure会拒绝跳转。 - 自定义处理器同时兼容了普通导航请求和AJAX请求场景,即使前端是第三方无法修改,也能自动触发跳转。
内容的提问来源于stack exchange,提问作者Sayak Mukhopadhyay
相关产品推荐
相关产品推荐

