Spring Data Rest+Spring Security下/login路径POST请求CORS异常排查
问题根因
- 你之前通过
RepositoryRestConfigurer配置的CORS规则仅生效于Spring Data Rest暴露的资源接口,/login请求由Spring Security过滤器链直接处理,不会走到Spring Data Rest的CORS处理逻辑。 - 浏览器对跨域非简单请求(比如JSON格式提交的POST登录请求)会先发送OPTIONS方法的预检请求,当前安全配置既没有在过滤器链前置CORS响应头逻辑,也没有对OPTIONS请求放行,导致预检请求被拦截,触发跨域报错。
- iOS、Android移动端原生请求不受浏览器同源策略约束,不会发送CORS预检请求,因此不受该问题影响。
修复步骤
1. 注册全局CORS配置源,开启Spring Security层的CORS支持
首先在你的安全配置类(或者任意配置类)中注册CorsConfigurationSource类型的Bean,定义全局生效的CORS规则,Spring Security会自动将CORS过滤器排在所有认证过滤器之前执行,确保CORS响应头在所有认证逻辑之前返回:
@Bean fun corsConfigurationSource(): CorsConfigurationSource { val corsConfig = CorsConfiguration().apply { // 按需调整允许的源、方法、头配置 allowedOriginPatterns = listOf("http://localhost:[*]") allowedMethods = listOf("GET", "POST", "PUT", "DELETE", "OPTIONS") allowedHeaders = listOf("*") // 要把自定义的Authorization响应头暴露给前端,否则前端拿不到token exposedHeaders = listOf("Authorization", "Content-Type") allowCredentials = true } return UrlBasedCorsConfigurationSource().apply { registerCorsConfiguration("/**", corsConfig) } }
然后在AppWebSecurityConfigurerAdapter的http配置块中,和csrf、sessionManagement配置同层级添加CORS开启配置:
http { csrf { disable() } // 新增这行开启Security层CORS支持 cors { } sessionManagement { sessionCreationPolicy = SessionCreationPolicy.STATELESS } // 其余原有过滤器、权限配置保持不变 addFilterAt<UsernamePasswordAuthenticationFilter>( JwtUsernameAndPasswordAuthenticationFilter( authenticationManager(), jwtConfiguration, secretKey, repository ) ) addFilterAfter<JwtUsernameAndPasswordAuthenticationFilter>(JwtTokenVerifier(jwtConfiguration, secretKey)) authorizeRequests { authorize(anyRequest, permitAll) } }
2. 放行所有OPTIONS预检请求
在authorizeRequests配置块最前面添加规则,允许所有OPTIONS请求直接通过,不需要走认证逻辑:
authorizeRequests { // 新增这行,优先放行预检请求 authorize(HttpMethod.OPTIONS, "/**", permitAll) authorize(anyRequest, permitAll) }
3. (可选兜底)自定义登录过滤器跳过OPTIONS请求
为了避免极端情况下CORS过滤器未拦截到OPTIONS请求,导致登录过滤器尝试解析空请求体报错,可以在JwtUsernameAndPasswordAuthenticationFilter的attemptAuthentication方法最开头加判断,直接放行OPTIONS请求:
override fun attemptAuthentication(request: HttpServletRequest?, response: HttpServletResponse?): Authentication { // 新增开头判断 if (request?.method == HttpMethod.OPTIONS.name()) { response?.status = HttpServletResponse.SC_OK // 直接返回空认证对象,不执行后续登录逻辑 return UsernamePasswordAuthenticationToken(null, null) } // 原有解析请求体、认证逻辑保持不变 val authenticationRequest: UsernameAndPasswordAuthenticationRequest? = request?.inputStream?.let { jacksonObjectMapper().readValue(it) } return authenticationManager.authenticate( UsernamePasswordAuthenticationToken(authenticationRequest?.username, authenticationRequest?.password) ) }
改完重启服务,Web端的/login跨域问题即可解决。
内容的提问来源于stack exchange,提问作者gd08xxx
相关产品推荐
相关产品推荐

