186

Windowsサービスと通信するWebアプリケーション(IISでホストされている)があります。WindowsサービスはASP.NetMVCWeb API(自己ホスト型)を使用しているため、JSONを使用してhttp経由で通信できます。Webアプリケーションは、なりすましを行うように構成されています。つまり、Webアプリケーションに要求を行うユーザーは、Webアプリケーションがサービスに要求を行うために使用するユーザーである必要があります。構造は次のようになります。

(赤で強調表示されているユーザーは、以下の例で参照されているユーザーです。)


Webアプリケーションは、HttpClient:を使用してWindowsサービスに要求を行います。

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

これにより、Windowsサービスに要求が送信されますが、資格情報は正しく渡されません(サービスはユーザーをとして報告しますIIS APPPOOL\ASP.NET 4.0)。これは私がしたいことではありません

上記のコードをWebClient代わりに使用するように変更すると、ユーザーの資格情報が正しく渡されます。

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

上記のコードを使用すると、サービスはユーザーをWebアプリケーションにリクエストしたユーザーとして報告します。

HttpClientクレデンシャルを正しく渡さない原因となっている実装で何が間違っているのですか(またはのバグHttpClientですか?)

私が使用したい理由HttpClientは、sでうまく機能する非同期APIがあるTaskのに対し、WebClient'sasycAPIはイベントで処理する必要があるためです。

4

8 に答える 8

166

HttpClient次のように、資格情報を自動的に渡すように構成できます。

var myClient = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
于 2013-04-15T15:20:56.007 に答える
73

私も同じ問題を抱えていました。次のSO記事で@tpeczekが行った調査のおかげで、同期ソリューションを開発しました。HttpClientを使用してASP.NETWebApiサービスに認証できません。

私のソリューションでは、を使用して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シリアライザーです。

于 2012-10-01T14:43:00.650 に答える
27

あなたがしようとしているのは、NTLMにIDを次のサーバーに転送させることですが、これはできません。偽装しかできず、ローカルリソースへのアクセスしかできません。マシンの境界を越えることはできません。Kerberos認証は、チケットを使用して委任(必要なもの)をサポートします。チェーン内のすべてのサーバーとアプリケーションが正しく構成され、Kerberosがドメインに正しく設定されている場合、チケットを転送できます。したがって、要するに、NTLMの使用からKerberosの使用に切り替える必要があります。

使用可能なWindows認証オプションとその機能の詳細については、http: //msdn.microsoft.com/en-us/library/ff647076.aspxから始めてください。

于 2012-08-31T20:29:54.833 に答える
23

さて、上記のすべての貢献者に感謝します。.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()は、私たちのなりすましのアイデンティティを取得します。これは、現在フローを抑制しているために取り上げられています。使用/破棄のため、これは呼び出し後にリセットされます。

今ではうまくいくようです、おい!

于 2016-10-11T06:51:47.433 に答える
15

.NET Coreでは、を使用して、認証されたユーザーのWindowsクレデンシャルをバックエンドサービスに渡すことがSystem.Net.Http.HttpClientできました。UseDefaultCredentials = trueWindowsIdentity.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);
    });
}
于 2018-02-06T18:49:14.310 に答える
5

Windowsサービスでインターネットにアクセスできるユーザーを設定した後、それは私のために働きました。

私のコードでは:

HttpClientHandler handler = new HttpClientHandler();
handler.Proxy = System.Net.WebRequest.DefaultWebProxy;
handler.Proxy.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
.....
HttpClient httpClient = new HttpClient(handler)
.... 
于 2017-11-01T08:45:19.040 に答える
3

さて、私はJoshounコードを取り、それをジェネリックにしました。SynchronousPostクラスにシングルトンパターンを実装する必要があるかどうかわかりません。多分もっと知識のある人が助けることができます。

実装

//あなたはあなた自身の具体的なタイプを持っていると思います。私の場合、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; }
    }

興味があれば、私の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パターンを使用しています。とにかく、上記のジェネリッククラスは本当に役に立ちます。

于 2012-11-27T21:54:10.317 に答える
0

web.configでのとをに設定しidentityますimpersonationtruevalidateIntegratedModeConfigurationfalse

<configuration>
  <system.web>
    <authentication mode="Windows" />
    <authorization>
      <deny users="?" />
    </authorization>
    <identity impersonate="true"/>
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" ></validation>
  </system.webServer>
</configuration>
于 2022-02-06T01:40:17.047 に答える