簡體   English   中英

"如何讓 HttpClient 與請求一起傳遞憑據?"

[英]How to get HttpClient to pass credentials along with the request?

我有一個與 Windows 服務對話的 Web 應用程序(托管在 IIS 中)。 Windows 服務使用 ASP.Net MVC Web API(自托管),因此可以使用 JSON 通過 http 進行通信。 Web 應用程序被配置為進行模擬,其想法是向 Web 應用程序發出請求的用戶應該是 Web 應用程序用來向服務發出請求的用戶。 結構如下所示:

(以紅色突出顯示的用戶是以下示例中提到的用戶。)<\/sub>


Web 應用程序使用HttpClient<\/code><\/a>向 Windows 服務發出請求:

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

您可以將HttpClient配置為自動傳遞憑據,如下所示:

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

我也遇到了同樣的問題。 由於@tpeczek 在以下 SO 文章中進行的研究,我開發了一個同步解決方案:無法使用 HttpClient 對 ASP.NET Web Api 服務進行身份驗證

我的解決方案使用WebClient ,正如您正確指出的那樣,它可以毫無問題地傳遞憑據。 HttpClient不起作用的原因是因為 Windows 安全性禁用了在模擬帳戶下創建新線程的能力(請參閱上面的 SO 文章) HttpClient通過任務工廠創建新線程從而導致錯誤。 另一方面, WebClient在同一線程上同步運行,從而繞過規則並轉發其憑據。

雖然代碼有效,但缺點是它不能異步工作。

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,它與 WebAPI 使用的 JSON 序列化程序相同。

您想要做的是讓 NTLM 將身份轉發到下一個服務器,但它不能這樣做 - 它只能進行模擬,這只能讓您訪問本地資源。 它不會讓你跨越機器邊界。 Kerberos 身份驗證通過使用票證支持委派(您需要什么),並且當鏈中的所有服務器和應用程序都正確配置並且 Kerberos 在域上正確設置時,票證可以被轉發。 因此,簡而言之,您需要從使用 NTLM 切換到 Kerberos。

有關可用的 Windows 身份驗證選項及其工作原理的更多信息,請訪問: http : //msdn.microsoft.com/en-us/library/ff647076.aspx

好的,感謝以上所有貢獻者。 我正在使用 .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() ,這是我們模擬的身份。 這是因為我們現在正在抑制流量。 由於 using/dispose 這在調用后被重置。

它現在似乎對我們有用,呸!

在 .NET Core 中,我設法通過使用WindowsIdentity.RunImpersonated獲得了一個System.Net.Http.HttpClientUseDefaultCredentials = true以將經過身份驗證的用戶的 Windows 憑據傳遞給后端服務。

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);
    });
}

在我在 Windows 服務中設置一個可以訪問互聯網的用戶后,它對我有用。

在我的代碼中:

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

好的,所以我采用了 Joshoun 代碼並使其通用。 我不確定是否應該在 SynchronousPost 類上實現單例模式。 也許知識淵博的人可以提供幫助。

執行

//我假設你有自己的具體類型。 就我而言,我首先將代碼與名為 FileCategory 的類一起使用

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();
    }
}

通用類在這里。 您可以傳遞任何類型

 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; } }

我的 Api 類看起來像這樣,如果你好奇的話

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 和帶有工作單元的 repo 模式。 無論如何,上面的泛型類確實有幫助。

在 webconfig 中將身份模擬設置為 true 並將 validateIntegratedModeConfiguration 設置為 false

<configuration>
  <system.web>
    <authentication mode="Windows" />
    <authorization>
      <deny users="?" />
    </authorization>
    <identity impersonate="true"/>
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" ></validation>
  </system.webServer>
</configuration>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM