7

Visual Studio 2012 を使用して、WPF (.NET 4.0) クライアントで ASP WebAPI (ASP MVC 4) アプリケーションを開発しています。クライアントはサーバーにログインする必要があります。FormsAuthentication と認証 Cookie を使用してログインします。ログインは、ASP MVC で既に正常に機能しています。

問題は、ログインがサーバー上で正常に実行され、Cookie がクライアントに送り返されても、サーバーへの後続の呼び出しで Cookie が送信されないことCookieContainerです。

コードの簡略版は次のとおりです。

クライアント

public async Task<UserProfile> Login(string userName, string password, bool rememberMe)
{           
    using (var handler = new HttpClientHandler() { CookieContainer = this.cookieContainer })
    using (var httpClient = new HttpClient(handler))
    {
        httpClient.BaseAddress = new Uri("http://localhost:50000/");

        httpClient.DefaultRequestHeaders.Accept.Add(
            new MediaTypeWithQualityHeaderValue("application/json"));

        var result = await httpClient.PostAsJsonAsync("api/auth/login", new
        {
            username = userName,
            password = password,
            rememberMe = rememberMe
        });

        result.EnsureSuccessStatusCode();

        var userProfile = await result.Content.ReadAsAsync<UserProfile>();

        if (userProfile == null)
            throw new UnauthorizedAccessException();

        return userProfile;
    }
}

public async Task<ExamSubmissionResponse> PostItem(Item item)
{
    using (var handler = new HttpClientHandler() { CookieContainer = this.cookieContainer })
    using (var httpClient = new HttpClient(handler))
    {
        httpClient.BaseAddress = new Uri("http://localhost:50000/");

        var result = await httpClient.PostAsJsonAsync("api/Items/", item);
    }
}

サーバ

[HttpPost]
public HttpResponseMessage Login(LoginModel model)
{
    if (this.ValidateUser(model.UserName, model.Password))
    {
        // Get user data from database

        string userData = JsonConvert.SerializeObject(userModel);

        var authTicket = new FormsAuthenticationTicket(
            1,
            model.UserName,
            DateTime.Now,
            DateTime.Now.AddMinutes(10 * 15),
            model.RememberMe,
            userData
        );              

        string ticket = FormsAuthentication.Encrypt(authTicket);
        var cookie = new CookieHeaderValue(FormsAuthentication.FormsCookieName, ticket);
        var response = Request.CreateResponse(HttpStatusCode.Created, userModel);
        response.Headers.AddCookies(new CookieHeaderValue[] { cookie });

        return response;
    }
    return null;
}

最初に、Fiddler2 を使用して問題をデバッグしました (http://localhost.fiddler:50000/ローカル トラフィックを表示するためにベース アドレスを " " として使用しました)。次に、フィドラーが干渉しているのではないかと疑ったので、Visual Studio 2012 でデバッグしました。

私が試して確認したこと:

  • サーバーは Login メソッドによって到達されます

  • ユーザーは、クライアントから送信されたデータで正常に認証されます

  • クッキーはサーバーに設定されています

  • Cookie は応答にあります (フィドラーで検証済み)

  • 操作後、Cookie は CookieContainer にあります。ここで奇妙なことがあります:コンテナー内の Cookie のドメインは "localhost" として設定されます (VS2012 デバッガーで検証)。" http://localhost:50000"ではないでしょうか? それを使用してコンテナのクッキーを取得しようとすると、cookieContainer.GetCookies(new Uri("http://localhost:50000"))何も返されません。使用してみるとcookieContainer.GetCookies(new Uri("localhost"))、無効な Uri エラーが発生します。ここで何が起こっているのかわかりません。

  • PostItemCookie は、要求が行われる直前にコンテナー内にあります。httpClient.PostAsJsonAsyncステートメントに到達すると、コンテナーは HttpClient で正しく設定されます。

  • Cookieはサーバーに送信されません(フィドラーで確認Application_PostAuthenticateRequestし、Global.asax.csのメソッドで確認しましたthis.Request.Cookies

のドメインの不一致が原因で Cookie が送信されていないと思われCookieContainerますが、そもそもドメインが適切に設定されていないのはなぜCookieContainerですか?

4

1 に答える 1

7

問題は、Web Api コントローラーから送り返す Cookie にパスを設定していないことです。

Cookie の送信先を制御するものは 2 つあります。

  1. クッキーのドメイン
  2. クッキーのパス

ドメインに関しては、コンセンサスは、ポート番号は Cookie ドメインを評価する際の要因であってはならないということです (ただし、その可能性はあります)。ポート番号がドメインに与える影響の詳細については、この質問を参照してください。

パスについて: Cookie は、ドメイン内の特定のパスに関連付けられています。あなたの場合、Web Api はパスを指定せずに Cookie を送信しています。デフォルトでは、Cookie は、Cookie が作成された要求/応答のパスに関連付けられます。

あなたの場合、Cookieにはパスがありますapi/auth/login。これは、Cookie がこのパスの子パス(より適切な用語がないため) に送信され、パスまたは兄弟パスには送信されないことを意味します。

これをテストするには、次を試してください。

cookieContainer.GetCookies(new Uri("http://localhost/api/auth/login")

これにより、Cookieが提供されます。これはそうすべきです:

cookieContainer.GetCookies(new Uri("http://localhost/api/auth/login/foo/bar")

一方、これらは Cookie を見つけられません。

cookieContainer.GetCookies(new Uri("http://localhost/")
cookieContainer.GetCookies(new Uri("http://localhost/api/")
cookieContainer.GetCookies(new Uri("http://localhost/api/auth/")
cookieContainer.GetCookies(new Uri("http://localhost/api/auth/foo")
cookieContainer.GetCookies(new Uri("http://localhost/api/Items/")

この問題を解決するには、応答を送信する前にパス「/」(または「/api」) を Cookie に追加するだけです。

...
string ticket = FormsAuthentication.Encrypt(authTicket);
var cookie = new CookieHeaderValue(FormsAuthentication.FormsCookieName, ticket);
cookie.Path = "/";
var response = Request.CreateResponse(HttpStatusCode.Created, userModel);
response.Headers.AddCookies(new CookieHeaderValue[] { cookie });
...
于 2013-07-05T06:43:33.567 に答える