如何从 C# WebApi 禁用对 MarkLogic 服务器的预检请求

Posted

技术标签:

【中文标题】如何从 C# WebApi 禁用对 MarkLogic 服务器的预检请求【英文标题】:How to disable preflight request to MarkLogic server from C# WebApi 【发布时间】:2020-09-11 18:44:48 【问题描述】:

技术栈如下

.Net core WebApi (C#) MarkLogic 9.0.8.2

我们开发了带有基本身份验证的 MarkLogic API,当从 C# WebApi 调用它时,我们在标头中传递凭据。

我们注意到 MarkLogic 访问日志有 2 个条目,即

xx.xxx.xxx.xx - - [18/May/2020:06:05:35 +0000] "GET /getcontent?query=test&offset=1&size=10&format=Extended&sortOrder=ASC&transform=&IncludeFacet=true HTTP/1.1" 401 209 - - xx.xxx.xxx.xx - MLUser [18/May/2020:06:05:35 +0000] "GET /getcontent?query=test&offset=1&size=10&format=Extended&sortOrder=ASC&transform =&IncludeFacet=true HTTP/1.1" 200 44229 - -

同样的请求带有 401 即未经授权,然后立即带有 200 即成功。

我们意识到从 C# WebAPI 调用 API 端点是在调用实际请求之前使用 OPTIONS 执行预检请求。

我们发现有发送内容类型为“文本/纯文本”的选项来解决这个问题,但我们不能,因为我们有 JSON 对象,然后我们需要做更多的事情来将文本解析为 JSON 对象。

当我们从 PostMan 调用时,我们面临同样的问题。

因此,当我们在 API 上收到大量请求时,ML API 在防火墙上返回 401 错误,然后 FW 会阻止所有请求,将其视为暴力攻击。

有什么方法可以禁用预检请求调用?

MarkLogicHttpClient _client;

var queryParams = new Dictionary<string, string>

     "query", searchRequest.Query ,
     "offset", searchRequest.Offset.ToString() ,
     "size", searchRequest.Size.ToString() ,
     "format", searchRequest.Format ,
     "sortOrder", searchRequest.SortOrder.ToString().ToUpper() ,
     "transform", transform 
;

var searchUri = QueryHelpers.AddQueryString("getcontent", queryParams);

var response = await _client.GetAsync(searchUri);

//Startup.cs

services.AddHttpClient<IMarkLogicClient, MarkLogicHttpClient>(client =>

    client.BaseAddress = MarkLogicEndpoint;
)
.ConfigurePrimaryHttpMessageHandler(() =>
    new HttpClientHandler
    
        Credentials = new NetworkCredential
        
            UserName = UserName,
            Password = Password
        
    
);

【问题讨论】:

如何将凭据添加到标头?可以提供代码吗? 您好布伦特,感谢您的回复。我在问题本身中添加了代码。 【参考方案1】:

HttpClient 仅在第一次收到来自服务器的带有 WWW-Authenticate 标头的 401 响应后才发送 Authorization 标头。

默认情况下,HttpClient 在授权每个请求之前等待 401 响应。

有两种方法可以减少 401 响应的数量。

设置HttpClientHandler.PreAuthenticate属性

services.AddHttpClient<IMarkLogicClient, MarkLogicHttpClient>(client =>

    client.BaseAddress = MarkLogicEndpoint;
)
.ConfigurePrimaryHttpMessageHandler(() =>
    new HttpClientHandler
    
        // cache the WWW-Authenticate response and send Authorize header
        // on all subsequent requests
        PreAuthenticate = true,
        Credentials = new NetworkCredential
        
            UserName = UserName,
            Password = Password
        
    );

或者,手动将 Authorize Header 添加到 HttpClient.DefaultRequestHeaders

services.AddHttpClient<IMarkLogicClient, MarkLogicHttpClient>(client =>

    // construct HTTP Basic Authorization header and send on every request
    // Do not add credentials to HttpClientHandler
    var authorization= "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(Username + ":" + Password));
    client.BaseAddress = MarkLogicEndpoint;
    client.DefaultRequestHeaders.Add("Authorization", authorization);
);

【讨论】:

您好布伦特,感谢您的示例回复。因此,当我使用 PreAutheticate 实现时,它仍然是第一次抛出 401 错误,然后是所有 200 个请求,但是如果在特定时间服务器中没有请求(我看到 2 分钟内是否没有请求),我再次看到 401 错误。使用第二种方法,我们永远不会收到 401 错误?我问是因为要测试,我需要遵循漫长的过程。我的意思是实际上我想禁用预检请求。 使用第二种方法,您应该期望没有预检和 401 你好布伦特,谢谢,所以你的意思是,是 httphandler 导致了预检请求? 是的,HttpClient 和 HttpHandler 实现 HTTP 基本身份验证的方式会导致零星的身份验证预检请求,即使启用了 PreAuthenticate。硬编码 Authorize 标头,如第二个示例所示,从不进行身份验证预检。 感谢布伦特的回复。现在 401 的频率非常低,但我仍然在访问日志文件中看到 401 错误,然后是 200 的相同请求。我重新启动服务也是为了确保它不会从缓存中获取任何内容。而且他们不是在第一个请求的开始,而是介于两者之间。硬编码也在做某种缓存?尽管 401 的数量很少,但似乎第二种方法仍在发生预检请求。

以上是关于如何从 C# WebApi 禁用对 MarkLogic 服务器的预检请求的主要内容,如果未能解决你的问题,请参考以下文章

从 c# 将 IFormFile 发布到 webAPI

从 C# 调用 WebApi 获取异常 HttpRequestException

如何使用 C# 按组策略检查 PowerShell 禁用

如何在 WebAPI 后端 C# 上接收 JSON 数据?

从统一 C# POST json 字符串到 webAPI; HttpClient 还是 UnityWebRequest?

C#进阶系列——WebApi 路由机制剖析:你准备好了吗?