You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React前端向Spring Security发送Login POST请求遇CORS拦截问题

问题解决:React登录POST请求CORS报错处理

问题描述

即使禁用了CSRF并配置了CORS,React前端仍无法发送登录POST请求。前端可以正常注册新用户,但登录请求失败,报错信息如下:

Access to XMLHttpRequest at 'http://localhost:8080/login' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

通过Postman可以正常登录,确认问题完全与CORS相关。

已尝试方案

  • 禁用CORS
  • 在Spring中添加允许localhost:3000的CORS配置,但未生效

代码片段

Spring WebSecurity配置

@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 {
        http
                .authorizeHttpRequests((requests) -> requests
                        //permit routes to / and /home
                        .antMatchers("/", "/home", "/signup", "/users", "/login")
                        .permitAll()
                        .anyRequest().
                        authenticated())
                .cors().configurationSource(corsConfigurationSource()).and()
                .csrf()
                .disable()
                .formLogin()
                .defaultSuccessUrl("https://www.apple.com/", true);
        return http.build();
    }

    @Bean
    public PasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("https://localhost:3000"));
        configuration.setAllowedMethods(Arrays.asList("GET","POST"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    public AuthenticationProvider authenticationProvider(){
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService);
        provider.setPasswordEncoder(bCryptPasswordEncoder());
        return provider;
    }
}

React Login.js代码

function Login() {
    const [formData, setFormData] = useState({
        username: '',
        password: ''
    });

    const [usernameError, setUsernameError] = useState('');
    const [passwordError, setPasswordError] = useState('');

    const onUserNameChange = (e) => {
        setFormData({...formData, username: e.target.value})
    }

    const onPasswordChange = (e) => {
        setFormData({...formData, password: e.target.value})
    }

    const login = async (username, password) => {
        const response = await axios.post("http://localhost:8080/login", {
            password: formData.username,
            username: formData.password
        })
    };

    return (
        <Container>
            <Typography variant='h4'>Login</Typography>
            <Stack spacing={2}>
                <TextField label='Username' onChange={onUserNameChange}/>
                {
                    usernameError ? <span style={{ color: 'red', fontSize: '12px'}}>{usernameError}</span> : ''
                }
                <TextField label='Password' type="password" onChange={onPasswordChange}/>
                {
                    passwordError ? <span style={{ color: 'red', fontSize: '12px'}}>{passwordError}</span> : ''
                }
                <Button variant='contained' onClick={login}>Login</Button>
            </Stack>
        </Container>
    );
}

export default Login;

解决办法

1. 修正CORS配置的协议匹配问题

Spring配置中允许的源是https://localhost:3000,但前端实际运行在http://localhost:3000,协议不匹配导致CORS规则不生效。修改corsConfigurationSource中的允许源:

configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000"));

2. 调整Spring Security配置顺序

将CORS配置放在权限校验之前,确保CORS过滤器优先处理请求:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
            .cors().configurationSource(corsConfigurationSource()).and()
            .csrf().disable()
            .authorizeHttpRequests((requests) -> requests
                    .antMatchers("/", "/home", "/signup", "/users", "/login")
                    .permitAll()
                    .anyRequest().authenticated())
            .formLogin()
            .defaultSuccessUrl("https://www.apple.com/", true);
    return http.build();
}

3. 修复前端请求参数错误

Login.js中axios请求的参数写反了username和password,修正后才能正常传递登录信息:

const login = async () => {
    try {
        const response = await axios.post("http://localhost:8080/login", {
            username: formData.username,
            password: formData.password
        });
        // 处理登录成功逻辑
    } catch (error) {
        console.error(error);
        // 处理错误提示
    }
};

额外优化建议

  • 为CORS配置添加允许所有请求头,避免其他头信息导致的拦截:
    configuration.setAllowedHeaders(Arrays.asList("*"));
    
  • Spring Security的formLogin默认接受application/x-www-form-urlencoded格式的请求,如果前端发送JSON格式,需要额外配置支持JSON登录,或者修改前端请求的Content-Type。

内容的提问来源于stack exchange,提问作者Brandon Lei

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 05:50:25