Spring Security问题:表单登录后无法通过AJAX访问/api接口
解决方案
问题出在你的apiFilterChain配置了无状态(STATELESS)会话管理,这会让Spring Security完全忽略已有的登录会话,所以即使通过表单登录创建了会话,访问/api/**时仍会强制要求Basic认证。要让表单登录的用户直接访问API,需要调整配置让API过滤器链同时支持会话认证和Basic认证。
调整SecurityConfig配置
修改apiFilterChain
将会话创建策略从STATELESS改为IF_REQUIRED,允许Spring Security读取已有的表单登录会话,同时保留Basic认证支持:
@Bean public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception { http .securityMatcher("/api/**") .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) // 保留Basic认证,供未通过表单登录的客户端使用 .httpBasic(Customizer.withDefaults()) // 允许使用已有的会话认证,不再强制无状态 .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)) // 配置CSRF,适配AJAX请求 .csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) ); return http.build(); }
确保formFilterChain的CSRF配置正确
表单登录后,AJAX请求API需要携带CSRF令牌,所以在formFilterChain中开启Cookie存储的CSRF令牌(允许前端读取):
@Bean public SecurityFilterChain formFilterChain(HttpSecurity http) throws Exception { http .securityMatcher("/**") .authorizeHttpRequests(auth -> auth .requestMatchers("/login").permitAll() .anyRequest().authenticated() ) .formLogin(form -> form .loginPage("/login") .defaultSuccessUrl("/home") .permitAll() ) .logout(logout -> logout.permitAll()) .csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) ); return http.build(); }
前端AJAX请求适配
由于启用了CSRF保护,AJAX请求/api/**时需要携带CSRF令牌。可以从Cookie中读取XSRF-TOKEN,并添加到请求头X-XSRF-TOKEN中:
// 工具函数:获取Cookie值 function getCookie(name) { let value = "; " + document.cookie; let parts = value.split("; " + name + "="); if (parts.length === 2) return parts.pop().split(";").shift(); } // 发起AJAX请求示例 $.ajax({ url: "/api/your-endpoint", type: "GET", headers: { "X-XSRF-TOKEN": getCookie("XSRF-TOKEN") }, success: function(response) { // 处理响应 } });
原理说明
- 调整会话策略后,
apiFilterChain会优先检查请求是否携带已登录的会话:如果有,直接使用会话中的认证信息;如果没有,再要求Basic认证。 - CSRF配置确保AJAX请求不会被Spring Security拦截,符合表单登录场景下的安全规范。
内容的提问来源于stack exchange,提问作者Andrzej Jankowski
相关产品推荐
相关产品推荐

