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

React Axios调用遇连接拒绝及CORS预请求401问题求助

问题分析与解决方案

核心问题

你的问题根源在于OWIN中间件执行顺序错误,导致CORS预请求(OPTIONS)被JWT认证中间件拦截,返回401 Unauthorized。同时初始的ERR_CONNECTION_REFUSED大概率是因为React开发服务器(默认3000端口)与API服务器(10000端口)跨域,且未正确配置CORS规则。

具体解决方案

1. 调整OWIN中间件顺序(关键)

OWIN中间件按注册顺序执行,必须保证CORS处理和OPTIONS请求放行逻辑在IP过滤、JWT认证之前执行,否则预请求会被后续拦截器直接拒绝。

修改你的.NET Framework 4.8 Web API启动类代码,调整中间件顺序并优化OPTIONS处理:

public void Configuration(IAppBuilder app)
{
    // 第一步:配置CORS规则,放在最前端
    app.UseCors(new CorsOptions
    {
        PolicyProvider = new CorsPolicyProvider
        {
            PolicyResolver = context =>
                Task.FromResult(new CorsPolicy
                {
                    AllowAnyMethod = true,
                    AllowAnyHeader = true,
                    Origins = { "http://localhost:10000", "http://localhost:3000" },
                    SupportsCredentials = true
                })
        }
    });

    // 第二步:直接放行OPTIONS预请求,跳过后续所有中间件
    app.Use(async (context, next) =>
    {
        if (context.Request.Method == "OPTIONS")
        {
            context.Response.StatusCode = 200;
            // 显式返回CORS响应头,避免浏览器报错
            context.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "http://localhost:3000", "http://localhost:10000" });
            context.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "Authorization", "Content-Type" });
            context.Response.Headers.Add("Access-Control-Allow-Methods", new[] { "GET", "POST", "PUT", "DELETE", "OPTIONS" });
            await context.Response.WriteAsync("");
            return; // 终止中间件链,不进入IP过滤和JWT认证
        }
        await next();
    });

    // 第三步:IP过滤中间件
    var authorizedIPs = new HashSet<string> { "::1" };
    foreach (var ip in Config.RestrictedAccesIps)
    {
        authorizedIPs.Add(ip);
    }
    app.Use(typeof(IpFilterMiddleware), authorizedIPs);

    // 第四步:JWT认证中间件
    if (Config.AuthMode == WebApi.GlobalConst.Auth0)
    {
        if (string.IsNullOrEmpty(Config.OrganizationId))
        {
            throw new ArgumentNullException("Organization Id missing");
        }

        var domain = Config.Domain;
        var apiIdentifier = Config.ApiIdentifier;
        var keyResolver = new OpenIdConnectSigningKeyResolver(domain);

        app.UseJwtBearerAuthentication(
            new JwtBearerAuthenticationOptions
            {
                AuthenticationMode = AuthenticationMode.Active,
                // 额外配置:确保非OPTIONS请求的Token校验正常
                Provider = new OAuthBearerAuthenticationProvider
                {
                    OnValidateIdentity = context =>
                    {
                        // 仅对非OPTIONS请求执行身份校验
                        if (context.Request.Method != "OPTIONS")
                        {
                            // 这里可以添加自定义校验逻辑
                        }
                        return Task.FromResult<object>(null);
                    }
                },
                TokenValidationParameters = new TokenValidationParameters()
                {
                    ValidAudience = apiIdentifier,
                    ValidateIssuer = true,
                    ValidateIssuerSigningKey = true,
                    ValidateLifetime = true,
                    ValidateAudience = true,
                    ValidIssuer = domain,
                    IssuerSigningKeyResolver = (token, securityToken, kid, parameters) => keyResolver.GetSigningKey(kid)
                }
            });
    }

    // 第五步:配置Web API路由
    var httpConfiguration = WebApiConfig.ConfigureWebApi();
    app.UseWebApi(httpConfiguration);
}

2. 优化React端请求代码

添加调试日志,确认Token有效性和请求细节:

const [products, setProducts] = useState([]);
const { getAccessTokenSilently } = useAuth0();

useEffect(() => {
    const fetchProducts = async () => {
        try {
            const accessToken = await getAccessTokenSilently();
            console.log("获取到的Token:", accessToken); // 对比Postman的Token,确认受众(Audience)一致
            const response = await axios.get('http://localhost:10000/******/Currencies', {
                headers: {
                    Authorization: `Bearer ${accessToken}`
                }
            });
            setProducts(response.data.data);
        } catch (error) {
            // 打印详细错误信息,方便排查
            console.error("请求错误:", error.response?.status, error.response?.data || error.message);
        }
    };

    fetchProducts();
}, [getAccessTokenSilently]);

3. 额外校验点

  • 确认Postman和React请求的URL路径完全一致(注意斜杠、路径后缀);
  • 检查Auth0配置的API Identifier与Token的aud字段是否匹配,不匹配会导致JWT校验失败;
  • 确保API服务器的IP过滤规则包含React开发服务器的IP(本地测试可保留::1)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 11:22:04