【问题标题】:HttpClient does not send cookies from CookieContainerHttpClient 不从 CookieContainer 发送 cookie
【发布时间】:2013-07-02 16:59:08
【问题描述】:

我正在使用 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);
    }
}

服务器

[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/”来查看本地流量)。然后我怀疑fiddler可能会干扰,所以我只是用Visual Studio 2012调试。

我已经尝试和验证的内容:

  • 通过登录方法到达服务器

  • 使用客户端发送的数据成功验证用户身份

  • 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 和 Global.asax.cs 中的Application_PostAuthenticateRequest 方法检查过,验证了this.Request.Cookies

我怀疑由于CookieContainer 中的域不匹配而没有发送cookie,但是为什么首先没有在CookieContainer 中设置域?

【问题讨论】:

  • 你能从 fiddler 中展示 cookie 的样子吗? 'new Uri("localhost")' 无效,因为您指定的是相对 URI,但没有说它是相对的。试试新的 Uri("localhost")。
  • 另外,你真的不应该在每个请求上重新创建 HttpClient。将其存储为字段并重复使用相同的实例。
  • 尝试在返回响应之前在 web.api 登录控制器中将 cookie 路径显式设置为“/”。这个我没有测试过,但是有可能是如果不设置路径就添加cookie的话,会默认为当前请求的路径。如果是这种情况,它将解释为什么 cookie 没有在以下请求中发送到不同的路径。

标签: asp.net cookies asp.net-web-api


【解决方案1】:

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

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

  1. cookie 的域
  2. cookie 的路径

关于域,共识似乎是端口号不应再(但仍然可能)是评估 cookie 域的一个因素。有关端口号如何影响域的更多信息,请参阅this question

关于路径: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 });
...

【讨论】:

  • 将“cookie.Path”设置为“/”就可以了。谢谢!
猜你喜欢
  • 1970-01-01
  • 2010-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-30
  • 2011-04-09
  • 2021-07-15
  • 1970-01-01
相关资源
最近更新 更多