HttpClient不会从CookieContainer发送Cookie

Art*_*nes 5 asp.net cookies asp.net-web-api

我正在使用Visual Studio 2012开发带有WPF(.NET 4.0)客户端的ASP WebAPI(ASP MVC 4)应用程序.客户端需要登录到服务器.我使用FormsAuthentication和身份验证cookie登录.登录已经在ASP MVC中正常工作.

问题是,尽管登录在服务器上成功执行并且cookie被发送回客户端,但是在后续调用服务器时不会发送cookie,即使它CookieContainer与auth cookie集重用.

这是代码的简化版本:

客户

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

服务器

[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;
}
Run Code Online (Sandbox Code Playgroud)

首先,我使用Fiddler2调试了问题(我使用基地址作为" http://localhost.fiddler:50000/"来查看本地流量).然后我怀疑小提琴手可能会干扰,所以我只是使用Visual Studio 2012进行了调试.

我试过并验证了什么:

  • Login方法到达服务器

  • 用户通过客户端发送的数据进行成功验证

  • cookie在服务器上设置

  • cookie在响应中(用fiddler验证)

  • 操作后,cookie位于CookieContainer中.这里有一个奇怪的事情:容器中cookie的域名设置为"localhost"(使用VS2012调试器验证).不应该是" http://localhost:50000"吗?当我尝试使用cookieContainer.GetCookies(new Uri("http://localhost:50000"))它获取容器的cookie时,什么都不返回.当我尝试使用cookieContainer.GetCookies(new Uri("localhost"))它时,给我一个无效的Uri错误.不知道这里发生了什么.

  • PostItem请求发出之前,cookie就在容器中.httpClient.PostAsJsonAsync到达语句时,容器在HttpClient中正确设置.

  • cookie没有发送到服务器(我用fiddler和Application_PostAuthenticateRequestGlobal.asax.cs 中的方法检查它,验证this.Request.Cookies)

我怀疑由于域名不匹配而没有发送cookie CookieContainer,但是为什么域名CookieContainer在第一时间没有设置?

use*_*080 5

您的问题是您没有在从Web Api控制器发回的cookie上设置任何路径.

有两件事可以控制发送cookie的位置:

  1. Cookie的域名
  2. cookie的路径

关于域名,共识似乎是端口号不再(但仍可能)成为评估cookie域的一个因素.有关端口号如何影响域的详细信息,请参阅此问题.

关于路径:Cookie与其域中的特定路径相关联.在您的情况下,Web Api发送cookie而不指定其路径.默认情况下,cookie将与创建cookie的请求/响应的路径相关联.

在您的情况下,cookie将具有路径api/auth/login.这意味着cookie将被发送到此路径的子路径(缺少更好的术语),但不会发送到路径或兄弟路径.

要测试这个,请尝试:

cookieContainer.GetCookies(new Uri("http://localhost/api/auth/login")
Run Code Online (Sandbox Code Playgroud)

这应该给你cookie.这应该是这样的:

cookieContainer.GetCookies(new Uri("http://localhost/api/auth/login/foo/bar")
Run Code Online (Sandbox Code Playgroud)

另一方面,这些将找不到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/")
Run Code Online (Sandbox Code Playgroud)

要解决此问题,只需在发送共鸣之前将路径"/"(或者可能是"/ 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 });
...
Run Code Online (Sandbox Code Playgroud)