React调用Spring Security保护的Spring Boot接口返回401未认证问题
问题诊断
你的场景核心矛盾是同源策略限制导致跨域请求时Authorization头无法被后端正确接收,同时Spring Security默认的CORS配置未适配跨域认证需求,最终请求被标记为匿名请求返回401。
解决方案
1. 完善Spring Security的CORS配置
当前仅调用.cors()但未自定义规则,Spring默认CORS配置不允许携带自定义认证头或凭证。需添加自定义CORS配置源:
@Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); // 允许React所在的本地开发源 config.setAllowedOrigins(Arrays.asList("http://localhost:3000")); // 允许所有请求方法 config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); // 允许携带认证头、内容类型头 config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type")); // 允许跨域请求携带凭证(认证头、Cookie等) config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); // 对所有接口路径应用CORS规则 source.registerCorsConfiguration("/**", config); return source; }
在SecurityFilterChain中关联自定义配置,替换原有的.cors():
http.authorizeHttpRequests(auth -> auth .requestMatchers(HttpMethod.POST , "/api/users").permitAll() .requestMatchers( "/api/authority").hasAuthority(Constants.ADMIN) .anyRequest().authenticated() ) // 绑定自定义CORS配置 .cors(cors -> cors.configurationSource(corsConfigurationSource())) .formLogin().permitAll() .and().logout().permitAll() .and().userDetailsService(userDetailsService) .csrf().disable() .httpBasic();
2. 修正React的Fetch请求写法
跨域请求携带Authorization头时,必须开启credentials选项,否则浏览器会自动丢弃认证头:
var myHeaders = new Headers(); myHeaders.append("Authorization", "Basic " + btoa("root:root")); const res = await fetch("http://localhost:7979/api/users", { method: 'GET', headers: myHeaders, credentials: 'include' // 关键:允许跨域携带认证凭证 }) console.log(res); const data = await res.json(); console.log(data);
3. 确认WSL与Windows的网络连通性(针对WSL运行React场景)
如果React在WSL中运行,需确保WSL能访问Windows侧的Spring服务:
- 检查WSL的
/etc/hosts是否包含127.0.0.1 localhost,或直接使用Windows本地IP(通过ipconfig查看以太网适配器WSL的IPv4地址)访问接口 - 在Spring的CORS配置中添加该IP作为允许的源,比如
"http://192.168.x.x:3000"
4. 验证认证头是否被正确传递
可在Spring后端添加拦截器,打印请求头确认Authorization是否被接收:
@Component public class RequestLoggingFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { System.out.println("Authorization Header: " + request.getHeader("Authorization")); filterChain.doFilter(request, response); } }
注册该过滤器:
@Bean FilterRegistrationBean<RequestLoggingFilter> loggingFilter() { FilterRegistrationBean<RequestLoggingFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new RequestLoggingFilter()); registrationBean.addUrlPatterns("/api/*"); return registrationBean; }
如果打印结果为null,说明浏览器未发送Authorization头,需重新检查CORS配置和Fetch的credentials选项。
关键注意事项
- 跨域请求会先发送OPTIONS预检请求,上述CORS配置已允许所有方法(包含OPTIONS),确保预检请求能正常通过
- 确认后端存储的密码是BCrypt加密后的对应值,比如
root加密后的字符串要与前端传入的明文匹配
内容的提问来源于stack exchange,提问作者Ledrake
相关产品推荐
相关产品推荐

