17370845950

解决React前端与Spring Security登录时的CORS跨域问题

本文详细阐述了react前端(如http://localhost:3000)在与spring security后端(如http://localhost:8080)进行登录认证时,常遇到的cors跨域请求阻塞问题。文章提供了全面的解决方案,通过在spring security中精确配置corsconfigurationsource,包括允许的源、方法、请求头和凭据,以确保登录请求(特别是涉及预检请求和凭据的请求)能够顺畅通过,实现前后端安全且高效的通信。

1. 理解CORS与Spring Security中的挑战

跨域资源共享(CORS)是一种浏览器安全机制,它限制了网页从不同域加载资源。当前端应用(例如,运行在http://localhost:3000的React应用)尝试向不同源的后端API(例如,运行在http://localhost:8080的Spring Boot应用)发送请求时,浏览器会执行CORS检查。对于简单的GET/POST请求,浏览器可能直接发送请求;但对于复杂的请求(如带有自定义头部、PUT/DELETE方法或需要凭据的请求),浏览器会先发送一个预检(Preflight)请求(HTTP OPTIONS方法),以确认服务器是否允许实际请求。

在Spring Security中处理登录请求时,由于通常涉及用户凭据(如Cookie或Authorization头部),这些请求被认为是“复杂请求”,因此会触发CORS预检。如果Spring Security没有正确配置CORS策略来响应这些预检请求,或者没有在响应中包含必要的CORS头部(如Access-Control-Allow-Origin),浏览器就会阻止实际的登录请求,导致常见的“CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource”错误。即使禁用了CSRF并尝试添加了CORS配置,如果配置不完整或不准确,问题依然会存在。

2. Spring Security CORS配置核心要素

解决React前端与Spring Security登录时的CORS问题,关键在于在Spring Security配置中提供一个全面且正确的CorsConfigurationSource。以下是配置的关键组成部分:

2.1 WebSecurityConfig 类结构

我们需要一个Spring配置类来定义安全链和CORS策略。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
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.Arrays;
import java.util.List;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

    @Autowired
    UserDetailsService userDetailsService;

    /**
     * 配置认证管理器
     */
    @Bean
    public AuthenticationManager authManager(HttpSecurity http) throws Exception {
        AuthenticationManagerBuilder authenticationManagerBuilder =
                http.getSharedObject(AuthenticationManagerBuilder.class);
        authenticationManagerBuilder.authenticationProvider(authenticationProvider());
        authenticationManagerBuilder.userDetailsService(userDetailsService);
        return authenticationManagerBuilder.build();
    }

    /**
     * 配置安全过滤链
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .csrf().disable() // 禁用CSRF,如果前端不发送CSRF令牌,通常需要禁用
                .cors()
                    // 明确指定CORS配置源,确保每次请求都获取最新配置
                    .configurationSource(request -> cors