我正在使用Visual Studio 2012开发带有WPF(.NET 4.0)客户端的ASP WebAPI(ASP MVC 4)应用程序。客户端需要登录到服务器。我使用FormsAuthentication和身份验证cookie登录。登录在ASP MVC中已经正常工作。
问题在于,尽管登录在服务器上成功执行并且cookie被发送回客户端,但是在后续调用服务器时不会发送cookie,即使CookieContainer
被重用auth cookie set。
以下是代码的简化版本:
客户端
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调试。
我尝试并验证过:
登录方法
使用客户端发送的数据
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
中首先设置?
答案 0 :(得分:5)
您的问题是您没有在从Web Api控制器发回的cookie上设置任何路径。
有两件事可以控制发送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 });
...