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

Spring Boot CORS配置问题:React前端请求返回401未授权

解决Spring Boot CORS配置异常与Basic Auth 401未授权问题

问题描述

React前端(http://localhost:3000)请求Spring Boot接口http://localhost:52700/greeting-javaconfig时,触发CORS拦截报错:

Access to fetch at 'http://localhost:52700/greeting-javaconfig' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status。

同时React端返回401未授权,但使用curl测试能正常得到200 OK响应,调整多种CORS配置后问题仍未解决。

现有代码问题分析

  1. CORS配置冲突:混合使用Servlet(WebMvcConfigurer)和Reactive(CorsWebFilter)两种CORS配置,且自定义CorsFilter未正确处理OPTIONS预检请求。
  2. Security配置混乱:同时使用WebSecurityCustomizer忽略所有请求、SecurityFilterChain定义认证规则,且混用Reactive的UserDetailsService与Servlet的HttpSecurity,导致认证逻辑冲突。
  3. @CrossOrigin注解冲突:控制器上的@CrossOrigin与全局CORS配置重复,可能导致规则覆盖。
  4. React请求配置缺失:未设置credentials: 'include',无法携带认证凭证,且Accept头与接口返回类型不匹配。

解决方案步骤

1. 统一Servlet栈CORS配置

删除Reactive相关CORS代码,保留全局WebMvc配置,确保支持凭证携带:

package com.example.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:3000") // 明确指定前端域名,不要用*(需携带凭证时)
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .allowCredentials(true) // 允许携带认证凭证
                .maxAge(3600); // 预检请求缓存时长
    }
}

删除自定义的CorsFilter(实现WebFilter的类),避免重复配置。

2. 修复Security配置(统一Servlet栈)

调整SecurityFilterChain,启用CORS和Basic Auth,处理预检请求的认证问题:

package com.example.config;

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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .cors() // 启用全局CORS配置
                .and()
                .authorizeHttpRequests(auth -> auth
                        .anyRequest().authenticated() // 所有请求需认证
                )
                .httpBasic() // 启用Basic Auth
                .authenticationEntryPoint(new Http403ForbiddenEntryPoint()) // 避免OPTIONS预检请求返回401
                .and()
                .csrf().disable(); // 跨域场景下禁用CSRF

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user = User
                .withUsername("user1")
                .password("{noop}user1Pass")
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(user); // 使用Servlet栈的用户服务
    }
}

说明:Http403ForbiddenEntryPoint会让未认证的请求返回403而非401,避免浏览器因401触发额外的认证弹窗,同时CORS配置会处理OPTIONS请求的跨域权限。

3. 调整React请求代码

确保携带凭证,匹配接口返回类型:

const url = 'http://localhost:52700/greeting-javaconfig';

const username = 'user1';
const password = 'user1Pass';

const credentials = btoa(username + ':' + password);

const options = {
    method: 'GET',
    headers: {
        'Authorization': 'Basic ' + credentials,
        'Content-Type': 'application/json',
        'Accept': 'application/json', // 接口返回JSON,匹配Accept头
        'cache-control': 'no-cache'
    },
    credentials: 'include' // 携带跨域凭证
};

fetch(url, options)
    .then(response => {
        if (!response.ok) {
            throw new Error(`请求失败,状态码:${response.status}`);
        }
        return response.json();
    })
    .then(data => console.log('响应数据:', data))
    .catch(error => console.error('请求错误:', error));

4. 移除控制器上的@CrossOrigin注解

删除BaseController中的@CrossOrigin(origins = "http://localhost:3000/"),避免与全局CORS配置冲突。

内容的提问来源于stack exchange,提问作者Meyer Buaharon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 15:34:57