深入分析自定义表单验证与Cookies
[Key words:Form,Security, FormsAuthentication, FormsAuthenticationTicket,cookies]
在ASP.net项目中,身份验证为我们提供了很多方便,而做为中小型的项目,一般都采用了表单验证。关于ASP.net里的表单验证,可以MSDN里的相关文章里找到很详细的说明。而且单单只是表单认证也是很简单的,这里就不去讨论它了。这里我想主要分析一下自己定义认证票据及Cookies之间的关系。
先给一个自己定义票据的例子:
{
string m_userData = this.m_userID.ToString()+","+this.m_loginName+","+this.m_userType.ToString();
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1,
"WebbUser",
System.DateTime.Now,
System.DateTime.Now.AddDays(30),
i_autoLogin,
m_userData,
"");
string encTicket = FormsAuthentication.Encrypt(ticket);
HttpContext.Current.Response.Cookies.Set(new HttpCookie("WebbUser", encTicket));
}
其中的m_userData可是自己定义的任何字符串,最后通过FormsAuthentication.Encrypt(ticket)加密然后添加到客户端。关于FormsAuthenticationTicket,在MSDN里也有相关说明。而我想说的是它的最后一个参数:MSDN里明确表示,要用"/",而通过多次测试,发现用其它任何有意义的字符串都可以,但不管你加了什么字符后,你所建立的表单票据都成了临时的了(多次测试,几乎让我疯掉了),而这里的时间参数根本无效即cookies建立在浏览器内存里,关闭浏览器后就无效,也就根本起为不了自动登录的功能。反到是把最后一个参数设定为空字符串,可以建立永久的Cookies。这里,我用了空串,让它建立永久的Cookies。
而最后我也用了Set Cookies而不是Add,这是有道理的。就算是第一次建立Cookies也可以建立成功,而用Add则会添加新的Cookies.其实我也不大明白这两者的区别。
取回自己定义的票据数据:当然,最后还要把取回来的数据设定成可用的格式。
{
if(User.Identity.IsAuthenticated)
{
string m_userData;
FormsIdentity id = (FormsIdentity)User.Identity;
FormsAuthenticationTicket ticket = id.Ticket;
m_userData = ticket.UserData;
if(m_userData!=null)
{
SetUserData(m_userData);
}
}
}
看上去这些很完美了,然而让我发疯的是它的注销。因为这里自己定义了票据,而这里的票据又是以Cookies添加在客户端的,所以用简单的:FormsAuthentication.SignOut();根本无效。于是简单的方法是想到自己处理Cookies,而处理Cookies就让我郁闷了。先看看Trace的结果:
Cookies Collection | |||||||||
---|---|---|---|---|---|---|---|---|---|
Name | Value | Size | |||||||
WebbUser | 28B93F4A0C8791830157006500620062005500730065007200000042C9ABA1550CC601014249109AE823C60131002C007700650062006200610064006D0069006E002C00410064006D0069006E0000000000 | 173 | |||||||
ASP.NET_SessionId | fvfyp4b2fchyvwy1pwowt5ur | 42 | |||||||
WebbUser | 28B93F4A0C8791830157006500620062005500730065007200000042C9ABA1550CC601014249109AE823C60131002C007700650062006200610064006D0069006E002C00410064006D0069006E0000000000 | 173 | |||||||
14 |
做一个小的测试:
{
HttpCookie m_cookie = Request.Cookies["WebbUser"];
m_cookie.Value = "Changed";
Response.Cookies.Set(m_cookie);
}
其后我得到的结果是:
Cookies Collection | |||||||||
---|---|---|---|---|---|---|---|---|---|
Name | Value | Size | |||||||
WebbUser | 28B93F4A0C8791830157006500620062005500730065007200000042C9ABA1550CC601014249109AE823C60131002C007700650062006200610064006D0069006E002C00410064006D0069006E0000000000 | 173 | |||||||
ASP.NET_SessionId | h1rdcxu21s4awxmupsiw5znc | 42 | |||||||
WebbUser | Changed | 16 |
Cookies Collection | |||||||||
---|---|---|---|---|---|---|---|---|---|
Name | Value | Size | |||||||
WebbUser | Changed | 16 | |||||||
ASP.NET_SessionId | 55icvo452ltsid454jtuklf0 | 42 |
猜测以下结论:
1、一个Cookie是在浏览器里,而另一个则是在Cookie文件里。
2、修改时,只修改了文件里的那一个,而浏览器里的没有变。但表单验证以文件为准,所以当我自己修改Cookies后,表单验证是失效了的。
再次登录,得到以下内容:
Cookies Collection | |||||||||
---|---|---|---|---|---|---|---|---|---|
Name | Value | Size | |||||||
WebbUser | Changed | 16 | |||||||
ASP.NET_SessionId | 4koyqb45dqm1qm551eahe245 | 42 | |||||||
WebbUser | 00E5C94AF9B385BA0157006500620062005500730065007200000064B91EC4E40CC60101643983BC7724C601320031002C00570075002E0043006F0075006E007400720079002C0043006C00690065006E00740000000000 | 185 |
这说明登录是失败的,因为根据上面的猜测,它只是在浏览器里建立一个临时的认证,而ASP.net要以文件认证,所以登录后,又被导入到登录页面。这让我很郁闷,因为我我的登录确实是重新设置了Cookies的。下面的测试更让人郁闷:
{
HttpCookieCollection m_cookies = Request.Cookies;
for(int i=0;i<m_cookies.Count;i++)
{
m_cookies[i].Value = DateTime.Now.ToString();
m_cookies[i].Expires = DateTime.Now.AddMinutes(-1);
Response.Cookies.Set(m_cookies[i]);
}
}
结果是:
Cookies Collection | |||||||||
---|---|---|---|---|---|---|---|---|---|
Name | Value | Size | |||||||
WebbUser | Changed | 16 | |||||||
ASP.NET_SessionId | 4koyqb45dqm1qm551eahe245 | 42 | |||||||
WebbUser | 00E5C94AF9B385BA0157006500620062005500730065007200000064B91EC4E40CC60101643983BC7724C601320031002C00570075002E0043006F0075006E007400720079002C0043006C00690065006E00740000000000 | 185 | |||||||
WebbUser | 12/30/2005 10:06:14 AM | 31 | |||||||
ASP.NET_SessionId | 12/30/2005 10:06:14 AM | 40 |
修改登录代码:
{
string m_userData = this.m_userID.ToString()+","+this.m_loginName+","+this.m_userType.ToString();
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1,
"WebbUser",
System.DateTime.Now,
System.DateTime.Now.AddDays(30),
i_autoLogin,
m_userData,
"");
string encTicket = FormsAuthentication.Encrypt(ticket);
HttpCookie m_cookie = HttpContext.Current.Request.Cookies.Get("WebbUser");
WaveHelper.TraceMsg(m_cookie.Value);
m_cookie.Value = encTicket;
//HttpContext.Current.Response.Cookies.Set(new HttpCookie("WebbUser", encTicket));
HttpContext.Current.Response.Cookies.Set(m_cookie);
}
测试输出:12/30/2005 10:14:32 AM Changed
说明取得了文件Cooikes里的正确值,但设置却只是临时,这是什么道理呢?为什么第一次登录的时候可以呢?而且第一次修改为"Changed"的时候如此的理所当然,以致于可以想到可以把它设定为任何想要的内容,可惜其后的所有设定都失败。
更为有意思的事,登录后,刷新页面可以得到页面里不断的添加新的Cooikes,而最多只有三个。不断刷新页面,得到下面的结果:
1 | |||||||||
---|---|---|---|---|---|---|---|---|---|
Name | Value | Size | |||||||
WebbUser | E7CCE30B0614E23101570065006200620055007300650072000000BC1DF89AEB0CC60101BC9D5C937E24C60131002C007700650062006200610064006D0069006E002C00410064006D0069006E0000000000 | 173 | |||||||
ASP.NET_SessionId | 1ck3eunw3fkiiuulbxvvrg45 | 42 | |||||||
WebbUser | CFCF23BA7B3631F801570065006200620055007300650072000000584EDA96EB0CC6010158CE3E8F7E24C60131002C007700650062006200610064006D0069006E002C00410064006D0069006E0000000000 | 173 | |||||||
WebbUser | D0E2E460A26850DC015700650062006200550073006500720000005625A99DEB0CC6010156A50D967E24C60131002C007700650062006200610064006D0069006E002C00410064006D0069006E0000000000 | 173 |
Cookies Collection | |||||||||
---|---|---|---|---|---|---|---|---|---|
Name | Value | Size | |||||||
WebbUser | D0E2E460A26850DC015700650062006200550073006500720000005625A99DEB0CC6010156A50D967E24C60131002C007700650062006200610064006D0069006E002C00410064006D0069006E0000000000 | 173 | |||||||
ASP.NET_SessionId | 1ck3eunw3fkiiuulbxvvrg45 | 42 | |||||||
WebbUser | CFCF23BA7B3631F801570065006200620055007300650072000000584EDA96EB0CC6010158CE3E8F7E24C60131002C007700650062006200610064006D0069006E002C00410064006D0069006E0000000000 | 173 | |||||||
WebbUser | 525BE5D26ED817F0015700650062006200550073006500720000001C9BB3ACEB0CC601011C1B18A57E24C60131002C007700650062006200610064006D0069006E002C00410064006D0069006E0000000000 | 173 |
3 | |||||||||
---|---|---|---|---|---|---|---|---|---|
Name | Value | Size | |||||||
WebbUser | 525BE5D26ED817F0015700650062006200550073006500720000001C9BB3ACEB0CC601011C1B18A57E24C60131002C007700650062006200610064006D0069006E002C00410064006D0069006E0000000000 | 173 | |||||||
ASP.NET_SessionId | 1ck3eunw3fkiiuulbxvvrg45 | 42 | |||||||
WebbUser | CFCF23BA7B3631F801570065006200620055007300650072000000584EDA96EB0CC6010158CE3E8F7E24C60131002C007700650062006200610064006D0069006E002C00410064006D0069006E0000000000 | 173 | |||||||
WebbUser | C13C21C845D22DDE015700650062006200550073006500720000001CA9DAB3EB0CC601011C293FAC7E24C60131002C007700650062006200610064006D0069006E002C00410064006D0069006E0000000000 | 173 |
Cookies Collection | |||||||||
---|---|---|---|---|---|---|---|---|---|
Name | Value | Size | |||||||
WebbUser | C13C21C845D22DDE015700650062006200550073006500720000001CA9DAB3EB0CC601011C293FAC7E24C60131002C007700650062006200610064006D0069006E002C00410064006D0069006E0000000000 | 173 | |||||||
ASP.NET_SessionId | 1ck3eunw3fkiiuulbxvvrg45 | 42 | |||||||
WebbUser | CFCF23BA7B3631F801570065006200620055007300650072000000584EDA96EB0CC6010158CE3E8F7E24C60131002C007700650062006200610064006D0069006E002C00410064006D0069006E0000000000 | 173 | |||||||
WebbUser | 1E7280709500E99E015700650062006200550073006500720000006A377CC2EB0CC601016AB7E0BA7E24C60131002C007700650062006200610064006D0069006E002C00410064006D0069006E0000000000 | 173 |
我已经没有办法区分它们的关系了,还想去猜测一下,哪几个在内存里,哪一个或者哪几个在文件里?关闭浏览器,再打开,得到下面的结果?两个?昏。。。。。
Cookies Collection | |||||||||
---|---|---|---|---|---|---|---|---|---|
Name | Value | Size | |||||||
WebbUser | 1E7280709500E99E015700650062006200550073006500720000006A377CC2EB0CC601016AB7E0BA7E24C60131002C007700650062006200610064006D0069006E002C00410064006D0069006E0000000000 | 173 | |||||||
WebbUser | 1E7280709500E99E015700650062006200550073006500720000006A377CC2EB0CC601016AB7E0BA7E24C60131002C007700650062006200610064006D0069006E002C00410064006D0069006E0000000000 | 173 | |||||||
ASP.NET_SessionId | 1nt5n0uwvcavxfev3xgolh55 | 42 | |||||||
14 |
修改后刷新,得到下面的结果:Cookies Collection | |||||||||
---|---|---|---|---|---|---|---|---|---|
Name | Value | Size | |||||||
WebbUser | 12/30/2005 10:55:57 AM | 31 | |||||||
ASP.NET_SessionId | fgc1pb45r3y4qx55a2dke2y3 | 42 | |||||||
WebbUser | 12/30/2005 10:56:06 AM | 31 | |||||||
WebbUser | 12/30/2005 10:56:13 AM | 31 |
几乎疯掉了,如果不能修改它的值,为什么可以在第一次修改,而其后所有的修改都失败,包括过期时间的修改。没道理。。。。。。。。。。而它对认证还起作用,也是没道理的。。。。。
我只能给出以下结论与解决方法了:1、这是ASP.net及浏览器间存在的问题。2、放弃使用自定义票据的表单验证。因为我并不想让所有出了问题的用户都去删除Cookies.
最后通过几次测试发现,可以用Add方法覆盖式的修改Cookies,但不管怎样,这里的Cookies与自己定义的表单验证是有问题的。最后为了安全起见,在项目里又添加了一个一般Cookies做为后备,这样在验证退出或者验证失败的时候,还可以用一般的Cookies再验证一次,然后强行退出要求用户登录。当然,如果登录不成功的话,那就只有手动删除Cookies了。伤心呀。。。。。。。。。。