为什么我的Http客户端在指定凭据时发出2个请求?

时间:2011-06-14 04:06:41

标签: c# http httpwebrequest basic-authentication

我创建了RESTful webservice(WCF),我检查每个请求的凭据。我的一个客户是Android应用程序,服务器端的一切似乎都很棒。我得到了请求,如果它有正确的标题 - 我处理它等等。

现在我创建了使用此服务的客户端应用程序。这就是我做GET的方式:

// Create the web request  
            var request = WebRequest.Create(Context.ServiceURL + uri) as HttpWebRequest;

            if (request != null)
            {
                request.ContentType = "application/json";

                // Add authentication to request  
                request.Credentials = new NetworkCredential(Context.UserName, Context.Password);

                // Get response  
                using (var response = request.GetResponse() as HttpWebResponse)
                {
                    // Get the response stream  
                    if (response != null)
                    {
                        var reader = new StreamReader(response.GetResponseStream());

                        // Console application output
                        var s = reader.ReadToEnd();

                        var serializer = new JavaScriptSerializer();
                        var returnValue = (T)serializer.Deserialize(s, typeof(T));

                        return returnValue;
                    }
                }
            }

所以,这段代码得到了我的资源并反序列化它。如你所见 - 我在通话中传递凭据。

然后在服务器端进行调试时,我注意到每次都有2个请求 - 一个没有认证头,然后服务器发回响应,第二个请求带有凭证。我认为这对我的服务器有害 - 我宁愿不做任何往返。我应该如何更改客户端,以免发生?查看Fiddler的截图

First BAD request

Second GOOD request

修改

这是我在Android上使用的JAVA代码 - 它不会进行双重调用:

MyHttpResponse response = new MyHttpResponse();
        HttpClient client = mMyApplication.getHttpClient();

        try
        {
            HttpGet request = new HttpGet(serviceURL + url);
            request.setHeader(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
            request.addHeader("Authorization", "Basic " + Preferences.getAuthorizationTicket(mContext));

            ResponseHandler<String> handler = new BasicResponseHandler();
            response.Body = client.execute(request, handler);
            response.Code = HttpURLConnection.HTTP_OK;
            response.Message = "OK";
        }
        catch (HttpResponseException e)
        {
            response.Code = e.getStatusCode();
            response.Message = e.getMessage();

            LogData.InsertError(mContext, e);
        }

3 个答案:

答案 0 :(得分:10)

初始请求不会指定用于身份验证的基本标头。此外,由于指定了领域,您必须从服务器获取该领域。所以你必须问一次:“嘿,我需要这些东西”,服务器就是“你是谁?回答的领域是'安全区'。” (因为领域在这里意味着什么)只是因为你在这里添加了它:

request.Credentials = new NetworkCredential(Context.UserName, Context.Password);

并不意味着每次都会确实附加到请求中。

然后你回复用户名/密码(在这种情况下,你正在做BASIC,所以它的base64编码为name:password),服务器解码它并说“好吧,你们都清楚,这是你的数据”。

这将会定期发生,而且你无法做很多事情。我建议你也打开HTTPS,因为身份验证是通过互联网以纯文本形式进行的。 (实际上你所展示的内容似乎是通过内联网,但如果你通过互联网进行https)。

以下是维基百科的链接,可能会对您有所帮助:http://en.wikipedia.org/wiki/Basic_access_authentication

答案 1 :(得分:9)

好的,我明白了。我手动设置HttpHeader而不是使用request.Credentials

request.Headers.Add(HttpRequestHeader.Authorization, "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(Context.UserName + ":" + Context.Password)));

现在我只看到了预期的单个请求。

答案 2 :(得分:0)

您可以选择使用PreAuthenticate property of HttpClientHandler。这将需要多两行

            var client = new HttpClient(new HttpClientHandler
            {
                Credentials = yourCredentials,
                PreAuthenticate = true
            });

使用这种方法,仅发送第一个请求而没有凭据,但是其余所有请求都可以。