为什么我的REST服务.NET客户端发送的每个请求都没有身份验证头,然后用身份验证头重试

本文关键字:身份验证 重试 然后 请求 NET 服务 REST 我的 客户端 为什么 | 更新日期: 2023-09-27 18:21:15

我们碰巧使用API运行REST web服务,要求客户端使用基本身份验证。我们用各种语言制作了一组简洁的示例,展示了如何与我们的服务接口。现在我正在查看该服务的IIS日志,发现以下模式经常发生:

  • 请求来了,被HTTP代码401拒绝
  • 重新发送同一请求并成功

看起来第一个请求是在没有Authorization标头的情况下发送的,然后第二个请求是用正确的标头发送的并成功。大多数时候,日志记录包含"用户代理",这与我们在.NET示例中植入的字符串相同。

所以我认为问题只出在.NET程序上。这个问题没有在我们的示例代码中重现,所以我认为用户以某种方式修改了代码或从头开始编写了自己的代码。

我们试着联系用户,但显然他们不想在研究上投入时间。因此,最好能找到最有可能导致.NET程序出现这种行为的场景。

他们为什么要这样做?为什么他们不在第一次尝试时附加标题?

为什么我的REST服务.NET客户端发送的每个请求都没有身份验证头,然后用身份验证头重试

这是HttpClientHttpWebRequest类的默认行为,通过以下方式公开。

注意:下面的文本解释了导致问题中所述问题的次优行为。很可能你不应该这样写代码。相反,请向下滚动至更正的代码

在这两种情况下,实例化一个NetworkCredenatial对象,并在中设置用户名和密码

var credentials = new NetworkCredential( username, password );

如果使用HttpWebRequest,请设置.Credentials属性:

webRequest.Credentials = credentials;

如果您使用HttpClient-将凭证对象传递到HttpClientHandler(此处更改的代码):

var client = new HttpClient(new HttpClientHandler() { Credentials = credentials })

然后运行Fiddler并启动请求。您将看到以下内容:

  • 发送请求时没有授权标头
  • 服务使用HTTP 401和WWW Authenticate:Basic realm="UrRealmHere"进行回复
  • 使用正确的授权标头重新发送请求(并成功)

这里解释了这种行为-客户端事先不知道服务需要Basic,并试图协商身份验证协议(如果服务需要Digest在打开状态下发送Basic标头是无用的,可能会危及客户端)。

注意:在这里,次优行为解释结束,并解释更好的方法。很可能您应该使用下面的代码,而不是上面的代码

对于已知服务需要Basic的情况,可以通过以下方式消除额外请求:

不要设置.Credentials,而是使用此处的代码手动添加标题。对用户名和密码进行编码:

var encoded = Convert.ToBase64String( Encoding.ASCII.GetBytes(
    String.Format( "{0}:{1}", username, password ) ) );

使用HttpWebRequest时,将其添加到标题中:

request.Headers.Add( "Authorization", "Basic " + encoded );

当使用HttpClient时,将其添加到默认标头:

client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue( "Basic", encoded );

当您这样做时,每次都会使用正确的授权标头发送请求。请注意,不应设置.Credentials,否则,如果用户名或密码错误,则会使用错误的凭据发送两次相同的请求,当然,两次都会生成HTTP 401。