繁体中文
设为首页
加入收藏
当前位置:.Net技术首页 >> Asp.Net开发 >> .NET2.0中Form验证的问题

.NET2.0中Form验证的问题

2007-09-15 08:00:00  作者:  来源:互联网  浏览次数:0  文字大小:【】【】【
简介:最近做一个SSO,使用Form认证方式,结果出了一个很让我郁闷的问题.先描述下问题: 用户登陆时,可以选择是否保存cookie,以便以后直接访问,如果选择否,则在不活动指定时间后,将自动注销. Web.config中的设置如下: ...
关键字:验证 问题 Form 2.0 NET

最近做一个SSO,使用Form认证方式,结果出了一个很让我郁闷的问题.先描述下问题:

用户登陆时,可以选择是否保存cookie,以便以后直接访问,如果选择否,则在不活动指定时间后,将自动注销.

Web.config中的设置如下:

这里只解释一下timeout参数,表示如果建立的是非持久性cookie,在不活动多长时间后,将需要重新验证,如果建立的是持久性cookie,那么该参数无效.

登陆时的建立验证cookie的代码如下:

HttpCookie authCookie = FormsAuthentication.GetAuthCookie("wiseman", true);

uthCookie.Expires = DateTime.Now.Add(TimeSpan.FromHours(1));

HttpContext.Current.Response.Cookies.Add(authCookie);

这里需要重点注意的就是FormsAuthentication.GetAuthCookie (string userName,bool createPersistentCookie)函数,在这里两个参数分别表示验证的用户名和是否持久保存cookie,上面我们的代码设置为true,表示将持久保持该cookie,过期时间是一个小时后.

但实际上,效果完全不是如此,用Request.IsAuthenticated你会发现,在很短的时间内,更确切的说是在5分钟以后,也就是我们在config中配置的timeout值,它的值就会变成false.

这意味着,我们在建立验证cookie时设置的过期时间没有起到效果,那么是cookie失效了吗?答案是否定的.在验证失败之后,我们依然可以取得cookie的值,这表示,在客户端建立的cookie没有问题,那么便是服务端的验证票据出了问题.

先来看看.net2.0中GetAuthCookie的代码.

private static HttpCookie GetAuthCookie(string userName, bool createPersistentCookie, string strCookiePath, bool hexEncodedTicket)

{

FormsAuthentication.Initialize();

if (userName == null)

{

userName = string.Empty;

}

if ((strCookiePath == null) || (strCookiePath.Length < 1))

{

strCookiePath = FormsAuthentication.FormsCookiePath;

}

FormsAuthenticationTicket ticket1 = new FormsAuthenticationTicket(2, userName, DateTime.Now, DateTime.Now.AddMinutes((double) FormsAuthentication._Timeout), createPersistentCookie, string.Empty, strCookiePath);

string text1 = FormsAuthentication.Encrypt(ticket1, hexEncodedTicket);

if ((text1 == null) || (text1.Length < 1))

{

throw new HttpException(SR.GetString("Unable_to_encrypt_cookie_ticket"));

}

HttpCookie cookie1 = new HttpCookie(FormsAuthentication.FormsCookieName, text1);

cookie1.HttpOnly = true;

cookie1.Path = strCookiePath;

cookie1.Secure = FormsAuthentication._RequireSSL;

if (FormsAuthentication._CookieDomain != null)

{

cookie1.Domain = FormsAuthentication._CookieDomain;

}

if (ticket1.IsPersistent)

{

cookie1.Expires = ticket1.Expiration;

}

return cookie1;

}

问题果然出在这里:

FormsAuthenticationTicket ticket1 = new FormsAuthenticationTicket(2, userName, DateTime.Now, DateTime.Now.AddMinutes((double) FormsAuthentication._Timeout), createPersistentCookie, string.Empty, strCookiePath);

这句代码表明,建立的验证票据过期时间只与web.config有关,即使我们在获得cookie后,修改其过期时间,也无法对票据的过期产生影响,这样产生的结果就是,客户端的cookie存在,但是服务器端的验证票据已经失效,最终,验证失败.

这里还需要提一下的是createPersistentCookie这个参数,当它为true时,并不表示票据将持久存在,而是表示可以跨越浏览器存在,简单点说就是即使你关了浏览器,只要过期时间不到,验证就依然是有效的.

实际上,很多人和我一样,在.net1.1时就使用了form认证,也使用过GetAuthCookie函数,处理方式也与我上面写的一样,并没出过什么问题,在CommunityServer2.0中,也是这么处理.那在.NET2.0中为什么不行呢?

原因其实很简单,因为在framework中,GetAuthCookie 函数1.1和2.0是不一样的,更准确的说是从2.0RTM版开始,GetAuthCookie函数进行了修改.下面给出.net1.1的GetAuthCookie函数代码:

public static HttpCookie GetAuthCookie(string userName, bool createPersistentCookie, string strCookiePath)

{

FormsAuthentication.Initialize();

if (userName == null)

{

userName = "";

}

if ((strCookiePath == null) || (strCookiePath.Length < 1))

{

strCookiePath = FormsAuthentication.FormsCookiePath;

}

FormsAuthenticationTicket ticket1 = new FormsAuthenticationTicket(1, userName, DateTime.Now, createPersistentCookie ? DateTime.Now.AddYears(50) : DateTime.Now.AddMinutes((double) FormsAuthentication._Timeout), createPersistentCookie, "", strCookiePath);

string text1 = FormsAuthentication.Encrypt(ticket1);

FormsAuthentication.Trace("ticket is " + text1);

if ((text1 == null) || (text1.Length < 1))

{

throw new HttpException(HttpRuntime.FormatResourceString("Unable_to_encrypt_cookie_ticket"));

}

HttpCookie cookie1 = new HttpCookie(FormsAuthentication.FormsCookieName, text1);

cookie1.Path = strCookiePath;

cookie1.Secure = FormsAuthentication._RequireSSL;

if (ticket1.IsPersistent)

{

cookie1.Expires = ticket1.Expiration;

}

return cookie1;

}

基本没什么区别,除了这句:

FormsAuthenticationTicket ticket1 = new FormsAuthenticationTicket(1, userName, DateTime.Now, createPersistentCookie ? DateTime.Now.AddYears(50) : DateTime.Now.AddMinutes((double) FormsAuthentication._Timeout), createPersistentCookie, "", strCookiePath);

这里在创建票据时,如果是创建持久性票据,过期时间设定为了50年,那么在50年的范围内,设定的cookie过期时间将影响是否验证成功,所以,如果最上面的代码在.NET1.1中运行的话,则是完全没有问题的.

上面说到CommunityServer2.0有这个问题,实际上CS2.0是基于.NET1.1的系统,所以在1.1环境下没有问题的;虽然它也提供了.NET2.0的solution,但是里面的代码并没有做相应的变动.

这就是为什么网上很多朋友在部署CS2.0时说无法保持自动登陆,而有许多朋友则表示没有问题.部署在1.1环境下的没有问题,而部署在2.0环境下的则会出现上述问题.

在MSDN2005中,并没有对GetAuthCookie函数做过多的说明,这也是造成许多程序员误解的原因,甚至被当做bug被提交.http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.ASPx?FeedbackID=102143

单从功能上来说,我不知道微软的这次改变有什么意义,直接导致的结果是我无法再使用GetAuthCookie,在不修改config文件的前提下,我将无法保持我的form验证能长期存在,唯一的解决方案就是把config文件中的timeout值设大.

据MS宣称这是出于安全的考虑,具体的原因还是不清楚,如果有哪位朋友知道,不妨帖出来让大家看下.

最后再给个问题的解决方案,其实算不上什么解决方案,也就是不用GetAuthCookie函数了,自己处理,如下:

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2, "wiseman",

DateTime.Now, DateTime.Now.AddHours(1), true, "", FormsAuthentication.FormsCookiePath);

string ticketEncrypted = FormsAuthentication.Encrypt(ticket);

HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, ticketEncrypted);

authCookie.HttpOnly = true;

authCookie.Path = FormsAuthentication.FormsCookiePath;

authCookie.Secure = FormsAuthentication.RequireSSL;

authCookie.Expires = ticket.Expiration;

HttpContext.Current.Response.Cookies.Add(authCookie);

手动建立票据和cookie,自己设定票据的过期时间,测试通过.

责任编辑:admin
相关文章