登录API端点无法访问:CORS跨域请求拦截问题求助
前端从http://localhost:3000发起登录请求到http://localhost:8082/login时触发CORS错误,API无响应。控制台报错:
Access to XMLHttpRequest at 'http://localhost:8082/login' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
AuthenticationService.js:10 AxiosError {message: 'Network Error', name: 'AxiosError', code: 'ERR_NETWORK', config: {…}, request: XMLHttpRequest, …}
后端登录端点代码如下:
@Controller @CrossOrigin( origins = "http://localhost:3000", allowCredentials = "true" ) @RequestMapping("/login") public class AuthenticationController { @Autowired AuthenticationManager authenticationManager; private static final Logger logger = LoggerFactory.getLogger(AuthenticationController.class); @PostMapping public ResponseEntity<?> login( @RequestBody Login login, HttpServletRequest request, HttpServletResponse response ){ logger.debug("Received login request for username: {}", login.getUsername()); System.out.println("AuthenticationCOntroller"); SecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository(); UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(login.getUsername(), login.getPassword()); Authentication authentication = authenticationManager.authenticate(token); if(authentication.isAuthenticated()){ User user = (User) authentication.getPrincipal(); Cookie cookie = CookieUtil.generateCookie(user); response.addCookie(cookie); return ResponseEntity.ok(authentication.getPrincipal()); } }
已尝试重构全部代码,但登录功能仍报错。
核心原因分析
- Spring Security拦截OPTIONS预检请求:若项目集成了Spring Security,默认会拦截OPTIONS类型的预检请求,导致
@CrossOrigin注解的CORS配置无法生效,预检请求得不到正确响应头。 - Controller方法分支无返回值:当前
login方法仅在认证通过时返回响应,若认证失败或抛出异常,无任何返回结果,会导致请求异常且无法添加CORS头。 - CORS配置优先级冲突:
@CrossOrigin是局部配置,若存在Spring Security全局CORS配置,局部配置会被覆盖,若全局配置缺失则CORS规则不生效。
解决方案
1. 配置Spring Security放行OPTIONS并启用全局CORS
在Spring Security配置类中添加CORS全局配置,确保预检请求被正确处理:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import java.util.List; @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .cors(cors -> cors.configurationSource(corsConfigurationSource())) .csrf(csrf -> csrf.disable()) // 前端未处理CSRF时可暂时禁用 .authorizeHttpRequests(auth -> auth .requestMatchers("/login").permitAll() .anyRequest().authenticated() ); return http.build(); } @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(List.of("http://localhost:3000")); config.setAllowedMethods(List.of("GET", "POST", "OPTIONS", "PUT", "DELETE")); config.setAllowedHeaders(List.of("*")); config.setAllowCredentials(true); config.setMaxAge(3600L); // 预检请求缓存时长 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return source; } }
2. 修复Controller方法的返回值逻辑
确保所有代码分支都有响应返回,避免无响应导致的异常:
@PostMapping public ResponseEntity<?> login( @RequestBody Login login, HttpServletRequest request, HttpServletResponse response ){ logger.debug("Received login request for username: {}", login.getUsername()); System.out.println("AuthenticationCOntroller"); UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(login.getUsername(), login.getPassword()); try { Authentication authentication = authenticationManager.authenticate(token); if(authentication.isAuthenticated()){ User user = (User) authentication.getPrincipal(); Cookie cookie = CookieUtil.generateCookie(user); response.addCookie(cookie); return ResponseEntity.ok(authentication.getPrincipal()); } return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials"); } catch (AuthenticationException e) { logger.error("Authentication failed for username: {}", login.getUsername(), e); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Authentication failed: " + e.getMessage()); } }
3. 移除Controller上的@CrossOrigin注解
使用全局CORS配置后,局部的@CrossOrigin可移除,避免配置冲突。
4. 验证预检请求
用curl发送OPTIONS请求验证响应头:
curl -X OPTIONS http://localhost:8082/login -H "Origin: http://localhost:3000" -i
检查响应头是否包含Access-Control-Allow-Origin: http://localhost:3000和Access-Control-Allow-Credentials: true。
内容的提问来源于stack exchange,提问作者Lucas

