अनुरोध के साथ क्रेडेंशियल पास करने के लिए HttpClient कैसे प्राप्त करें?


164

मेरे पास एक वेब एप्लिकेशन (IIS में होस्ट किया गया) है जो एक विंडोज सेवा से बात करता है। Windows सेवा ASP.Net MVC वेब API (स्व-होस्टेड) ​​का उपयोग कर रही है, और इसलिए JSON का उपयोग करके http से अधिक के साथ संचार किया जा सकता है। वेब एप्लिकेशन को प्रतिरूपण करने के लिए कॉन्फ़िगर किया गया है, यह विचार यह है कि उपयोगकर्ता जो वेब एप्लिकेशन के लिए अनुरोध करता है, वह उपयोगकर्ता होना चाहिए जो वेब एप्लिकेशन सेवा का अनुरोध करने के लिए उपयोग करता है। संरचना इस तरह दिखती है:

(लाल रंग में हाइलाइट किया गया उपयोगकर्ता नीचे के उदाहरणों में संदर्भित किया जा रहा है।)


वेब एप्लिकेशन विंडोज सेवा का अनुरोध करता है HttpClient:

var httpClient = new HttpClient(new HttpClientHandler() 
                      {
                          UseDefaultCredentials = true
                      });
httpClient.GetStringAsync("http://localhost/some/endpoint/");

यह विंडोज सेवा के लिए अनुरोध करता है, लेकिन सही ढंग से क्रेडेंशियल्स को पारित नहीं करता है (सेवा उपयोगकर्ता को रिपोर्ट करती है IIS APPPOOL\ASP.NET 4.0)। यह वह नहीं है जो मैं होना चाहता हूं

अगर मैं उपर्युक्त कोड को WebClientबदले में उपयोग करता हूं , तो उपयोगकर्ता के क्रेडेंशियल सही तरीके से पारित हो जाते हैं:

WebClient c = new WebClient
                   {
                       UseDefaultCredentials = true
                   };
c.DownloadStringAsync(new Uri("http://localhost/some/endpoint/"));

उपरोक्त कोड के साथ, सेवा उपयोगकर्ता को उस उपयोगकर्ता के रूप में रिपोर्ट करती है जिसने वेब एप्लिकेशन के लिए अनुरोध किया था।

मैं उस HttpClientकार्यान्वयन के साथ क्या गलत कर रहा हूं जिससे यह सही ढंग से क्रेडेंशियल्स पास नहीं कर रहा है (या यह बग के साथ है HttpClient)?

इसका उपयोग करने का कारण HttpClientयह है कि इसमें एक async API है जो Tasks के साथ अच्छी तरह से काम करता है , जबकि WebClient's asyc API को इवेंट्स के साथ संभाला जाना चाहिए।


के संभावित डुप्लिकेट stackoverflow.com/q/10308938/1045728
टॉमी Grovnes

ऐसा लगता है कि HttpClient और WebClient अलग-अलग चीजों को DefaultCredentials मानते हैं। क्या आपने HttpClient.setCredentials (...) की कोशिश की?
जर्मन अर्लिंगटन

BTW, WebClient DownloadStringTaskAsyncमें .Net 4.5 है, जिसका उपयोग async / प्रतीक्षा के साथ भी किया जा सकता है
LB

1
@ DeutschnArlington: के HttpClientपास कोई SetCredentials()विधि नहीं है । क्या आप मुझे बता सकते हैं कि आपका क्या मतलब है?
एड्रिएनबैंक

4
ऐसा प्रतीत होता है कि इसे ठीक कर दिया गया है (.net 4.5.1)? मैंने new HttpClient(new HttpClientHandler() { AllowAutoRedirect = true, UseDefaultCredentials = true }विंडोज-प्रमाणित उपयोगकर्ता द्वारा एक्सेस किए गए वेब सर्वर पर बनाने की कोशिश की , और वेब साइट ने उसके बाद एक और दूरस्थ संसाधन के लिए प्रमाणित किया (ध्वज सेट के बिना प्रमाणित नहीं होगा)।
GSerg

जवाबों:


67

मुझे भी यही समस्या हो रही थी। मैंने निम्न SO लेख में @tpeczek द्वारा किए गए शोध के लिए एक समकालिक समाधान विकसित किया है: HttpClient के साथ ASP.NET वेब एप सेवा को प्रमाणित करने में असमर्थ

मेरा समाधान एक का उपयोग करता है WebClient, जिसे आपने सही ढंग से नोट किया है बिना किसी समस्या के प्रमाणिकता के। इसका कारण HttpClientयह नहीं है कि विंडोज सुरक्षा एक अव्यवस्थित खाते के तहत नए थ्रेड बनाने की क्षमता को अक्षम करने के कारण है (ऊपर SO लेख देखें।) HttpClientटास्क फैक्ट्री के माध्यम से नए थ्रेड्स बनाता है, जिससे त्रुटि होती है। WebClientदूसरी ओर, समान रूप से एक ही धागे पर चलता है जिससे नियम को दरकिनार किया जाता है और अपनी साख को आगे बढ़ाया जाता है।

हालांकि कोड काम करता है, नकारात्मक पक्ष यह है कि यह async काम नहीं करेगा।

var wi = (System.Security.Principal.WindowsIdentity)HttpContext.Current.User.Identity;

var wic = wi.Impersonate();
try
{
    var data = JsonConvert.SerializeObject(new
    {
        Property1 = 1,
        Property2 = "blah"
    });

    using (var client = new WebClient { UseDefaultCredentials = true })
    {
        client.Headers.Add(HttpRequestHeader.ContentType, "application/json; charset=utf-8");
        client.UploadData("http://url/api/controller", "POST", Encoding.UTF8.GetBytes(data));
    }
}
catch (Exception exc)
{
    // handle exception
}
finally
{
    wic.Undo();
}

नोट: NuGet पैकेज की आवश्यकता है: Newtonsoft.Json, जो एक ही JSON सीरियलाइज़र WebAPI उपयोग करता है।


1
मैंने अंत में कुछ ऐसा ही किया, और यह वास्तव में अच्छा काम करता है। एसिंक्रोनस समस्या कोई समस्या नहीं है, क्योंकि मैं कॉल को ब्लॉक करना चाहता हूं।
एड्रिएनबैंक

136

आप HttpClientइस तरह से क्रेडेंशियल पास करने के लिए कॉन्फ़िगर कर सकते हैं :

var myClient = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });

11
मुझे पता है कि कैसे करना है। व्यवहार वह नहीं है जो मैं चाहता हूं (जैसा कि प्रश्न में कहा गया है) - "यह विंडोज सेवा के लिए अनुरोध करता है, लेकिन सही ढंग से क्रेडेंशियल्स पास नहीं करता है (सेवा उपयोगकर्ता को IIS APPPOOL \ ASP.NET 4.0 के रूप में रिपोर्ट करता है।) वह नहीं है जो मैं होना चाहता हूं। ”
एड्रिअनबैंक

4
यह मेरे मुद्दे को ठीक करने के लिए लगता है जहां आईआईएस में केवल विंडोज़ प्रमाणीकरण सक्षम है। यदि आपको बस कुछ कानूनी प्रमाणिकता की आवश्यकता है, तो इसे करना चाहिए।
टिमर्ज़

सुनिश्चित नहीं है कि यह प्रतिरूपण / डेलिगेशन परिदृश्यों में WebClient के समान कार्य करता है। उपरोक्त समाधान के साथ HttpClient का उपयोग करते समय मुझे "लक्ष्य प्रिंसिपल नाम गलत है" मिलता है, लेकिन इसी तरह के सेटअप के साथ WebClient का उपयोग करना उपयोगकर्ता के क्रेडेंशियल्स से गुजरता है।
पेडर राइस

इसने मेरे लिए काम किया और लॉग सही उपयोगकर्ता दिखाते हैं। हालांकि, चित्र में डबल हॉप के साथ, मुझे एनटीएलएम के साथ अंतर्निहित प्रमाणीकरण योजना के रूप में काम करने की उम्मीद नहीं थी, लेकिन यह काम करता है।
नितिन रस्तोगी

एस्पनेट कोर के नवीनतम संस्करण का उपयोग करके समान कैसे करें? (2.2)। अगर किसी को पता हो तो ...
निको

26

आप जो करने की कोशिश कर रहे हैं वह अगले सर्वर पर पहचान को आगे बढ़ाने के लिए NTLM है, जो यह नहीं कर सकता है - यह केवल प्रतिरूपण कर सकता है जो केवल आपको स्थानीय संसाधनों तक पहुंच प्रदान करता है। यह आपको एक मशीन सीमा पार नहीं करने देगा। करबरोस प्रमाणीकरण टिकटों का उपयोग करके शिष्टमंडल (आपको क्या चाहिए) का समर्थन करता है, और टिकट को तब अग्रेषित किया जा सकता है जब श्रृंखला के सभी सर्वर और एप्लिकेशन सही ढंग से कॉन्फ़िगर किए गए हों और करबरोस को डोमेन पर सही ढंग से सेट किया गया हो। तो, संक्षेप में आपको NTLM का उपयोग कर केबरोस से स्विच करने की आवश्यकता है।

आपके लिए उपलब्ध Windows प्रमाणीकरण विकल्पों पर अधिक जानकारी के लिए और वे कैसे काम करना शुरू करते हैं: http://msdn.microsoft.com/en-us/library/ff647076.aspx


3
" NTLM अगले सर्वर पर पहचान को अग्रेषित करने के लिए, जो यह नहीं कर सकता " - कैसे उपयोग करते समय यह ऐसा करता है WebClient? यह बात मुझे समझ नहीं आता है - अगर यह संभव नहीं है, किस तरह यह है यह कर?
एड्रियनबैंक

2
वेब क्लाइंट का उपयोग करते समय यह अभी भी केवल एक कनेक्शन है, क्लाइंट और सर्वर के बीच। यह उस सर्वर (1 हॉप) पर उपयोगकर्ता को लागू कर सकता है, लेकिन उन क्रेडेंशियल्स को किसी अन्य मशीन (2 हॉप्स - क्लाइंट टू सर्वर पर 2 सर्वर) पर अग्रेषित नहीं कर सकता है। उसके लिए आपको प्रतिनिधिमंडल की आवश्यकता है।
BlackSpy

1
आप जिस तरीके से करने की कोशिश कर रहे हैं उसे पूरा करने का एकमात्र तरीका यह है कि उपयोगकर्ता को अपने ASP.NET एप्लिकेशन पर कस्टम डायलॉग बॉक्स में अपना उपयोगकर्ता नाम और पासवर्ड टाइप करने के लिए प्राप्त करें, उन्हें स्ट्रिंग्स के रूप में संग्रहीत करें और फिर उपयोग करें जब आप अपने वेब एपीआई प्रोजेक्ट से जुड़ते हैं तो अपनी पहचान सेट करने के लिए। अन्यथा आपको NTLM को ड्रॉप करने और Kerberos पर जाने की आवश्यकता है, ताकि आप वेब API प्रोजेक्ट में Kerboros टिकट पास कर सकें। मैं अपने मूल उत्तर में संलग्न लिंक को पढ़ने की अत्यधिक सलाह देता हूं। आप जो करने की कोशिश कर रहे हैं उसे शुरू करने से पहले आपको विंडोज़ प्रमाणीकरण की एक मजबूत समझ की आवश्यकता होती है।
12

2
@ ब्लेकस्पी: मुझे विंडोज ऑथेंटिकेशन के साथ बहुत अनुभव है। मैं यह समझने की कोशिश कर रहा हूं WebClientकि NTLM क्रेडेंशियल्स पर पास क्यों हो सकता है, लेकिन HttpClientनहीं। मैं ASP.Net प्रतिरूपण का उपयोग करके इसे प्राप्त कर सकता हूं, और Kerberos का उपयोग करने या उपयोगकर्ता नाम / पासवर्ड संग्रहीत करने के लिए नहीं। हालांकि यह केवल साथ काम करता है WebClient
एड्रियनबैंक

1
पाठ के रूप में उपयोगकर्ता नाम और पासवर्ड को पास किए बिना 1 से अधिक हॉप पर प्रतिरूपण करना असंभव होना चाहिए। यह प्रतिरूपण के नियमों को तोड़ता है, और NTLM इसकी अनुमति नहीं देगा। WebClient आपको 1 हॉप कूदने की अनुमति देता है क्योंकि आप क्रेडेंशियल पास करते हैं और बॉक्स पर उस उपयोगकर्ता के रूप में चलते हैं। यदि आप सुरक्षा लॉग को देखते हैं तो आप लॉगिन देखेंगे - उपयोगकर्ता सिस्टम में लॉग करता है। आप उस मशीन से उस उपयोगकर्ता के रूप में तब तक नहीं चल सकते जब तक कि आपने पाठ के रूप में क्रेडेंशियल्स पारित नहीं किए हैं और अगले बॉक्स पर लॉग करने के लिए किसी अन्य वेब उदाहरण का उपयोग करें।
BlackSpy

17

ठीक है, इसलिए उपरोक्त सभी योगदानकर्ताओं के लिए धन्यवाद। मैं .NET 4.6 का उपयोग कर रहा हूं और हमारे पास भी यही मुद्दा था। मैंने System.Net.Httpविशेष रूप से डिबगिंग में समय बिताया HttpClientHandler, और निम्नलिखित पाया:

    if (ExecutionContext.IsFlowSuppressed())
    {
      IWebProxy webProxy = (IWebProxy) null;
      if (this.useProxy)
        webProxy = this.proxy ?? WebRequest.DefaultWebProxy;
      if (this.UseDefaultCredentials || this.Credentials != null || webProxy != null && webProxy.Credentials != null)
        this.SafeCaptureIdenity(state);
    }

इसलिए यह आकलन करने के बाद कि ExecutionContext.IsFlowSuppressed()शायद अपराधी था, मैंने हमारे प्रतिरूपण कोड को निम्न प्रकार से लपेटा:

using (((WindowsIdentity)ExecutionContext.Current.Identity).Impersonate())
using (System.Threading.ExecutionContext.SuppressFlow())
{
    // HttpClient code goes here!
}

SafeCaptureIdenity(मेरी वर्तनी की गलती नहीं) के अंदर कोड , WindowsIdentity.Current()जो हमारे प्रतिरूपण की पहचान है। इसे उठाया जा रहा है क्योंकि हम अब प्रवाह को दबा रहे हैं। उपयोग / निपटान के कारण यह मंगलाचरण के बाद रीसेट हो जाता है।

अब यह हमारे लिए काम करने लगता है, काश!


2
इस विश्लेषण को करने के लिए बहुत-बहुत धन्यवाद। इससे मेरी स्थिति भी ठीक हो गई। अब मेरी पहचान को अन्य वेब एप्लिकेशन पर सही ढंग से पास किया गया है! तुमने मुझे काम के घंटे बचाए! मुझे आश्चर्य है कि यह टिक गिनती पर अधिक नहीं है।
justdan23

मुझे केवल जरूरत थी using (System.Threading.ExecutionContext.SuppressFlow())और मुद्दा मेरे लिए हल हो गया था!
ZX9

10

नेट कोर में, मैं एक पाने में कामयाब रहे System.Net.Http.HttpClientसाथ UseDefaultCredentials = trueका उपयोग करके एक वापस अंत सेवा के लिए प्रमाणीकृत उपयोगकर्ता के Windows क्रेडेंशियल्स के माध्यम से पारित करने के लिए WindowsIdentity.RunImpersonated

HttpClient client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true } );
HttpResponseMessage response = null;

if (identity is WindowsIdentity windowsIdentity)
{
    await WindowsIdentity.RunImpersonated(windowsIdentity.AccessToken, async () =>
    {
        var request = new HttpRequestMessage(HttpMethod.Get, url)
        response = await client.SendAsync(request);
    });
}

4

विंडोज सेवा में इंटरनेट एक्सेस के साथ एक उपयोगकर्ता को सेट करने के बाद मैंने इसके लिए काम किया।

मेरे कोड में:

HttpClientHandler handler = new HttpClientHandler();
handler.Proxy = System.Net.WebRequest.DefaultWebProxy;
handler.Proxy.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
.....
HttpClient httpClient = new HttpClient(handler)
.... 

3

ठीक है तो मैंने जोशुन कोड लिया और इसे सामान्य बना दिया। मुझे यकीन नहीं है कि मुझे सिंक्रोनसपोस्ट क्लास पर सिंगलटन पैटर्न लागू करना चाहिए। शायद कोई और जानकार मदद कर सकता है।

कार्यान्वयन

// मेरा मानना ​​है कि आपके पास अपना ठोस प्रकार है। मेरे मामले में मैं पहली बार FileCategory नामक वर्ग के साथ कोड का उपयोग कर रहा हूं

FileCategory x = new FileCategory { CategoryName = "Some Bs"};
SynchronousPost<FileCategory>test= new SynchronousPost<FileCategory>();
test.PostEntity(x, "/api/ApiFileCategories"); 

यहां जेनेरिक क्लास। आप किसी भी प्रकार से पास कर सकते हैं

 public class SynchronousPost<T>where T :class
    {
        public SynchronousPost()
        {
            Client = new WebClient { UseDefaultCredentials = true };
        }

        public void PostEntity(T PostThis,string ApiControllerName)//The ApiController name should be "/api/MyName/"
        {
            //this just determines the root url. 
            Client.BaseAddress = string.Format(
         (
            System.Web.HttpContext.Current.Request.Url.Port != 80) ? "{0}://{1}:{2}" : "{0}://{1}",
            System.Web.HttpContext.Current.Request.Url.Scheme,
            System.Web.HttpContext.Current.Request.Url.Host,
            System.Web.HttpContext.Current.Request.Url.Port
           );
            Client.Headers.Add(HttpRequestHeader.ContentType, "application/json;charset=utf-8");
            Client.UploadData(
                                 ApiControllerName, "Post", 
                                 Encoding.UTF8.GetBytes
                                 (
                                    JsonConvert.SerializeObject(PostThis)
                                 )
                             );  
        }
        private WebClient Client  { get; set; }
    }

यदि आप उत्सुक हैं, तो मेरा आपी वर्ग इस तरह दिखता है

public class ApiFileCategoriesController : ApiBaseController
{
    public ApiFileCategoriesController(IMshIntranetUnitOfWork unitOfWork)
    {
        UnitOfWork = unitOfWork;
    }

    public IEnumerable<FileCategory> GetFiles()
    {
        return UnitOfWork.FileCategories.GetAll().OrderBy(x=>x.CategoryName);
    }
    public FileCategory GetFile(int id)
    {
        return UnitOfWork.FileCategories.GetById(id);
    }
    //Post api/ApileFileCategories

    public HttpResponseMessage Post(FileCategory fileCategory)
    {
        UnitOfWork.FileCategories.Add(fileCategory);
        UnitOfWork.Commit(); 
        return new HttpResponseMessage();
    }
}

मैं काम की इकाई के साथ Ninject, और रेपो पैटर्न का उपयोग कर रहा हूं। वैसे भी, सामान्य वर्ग के ऊपर वास्तव में मदद करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.