zoukankan      html  css  js  c++  java
  • .NET2.0中Form验证的问题.

    最近做一个SSO,使用Form认证方式,结果出了一个很让我郁闷的问题.先描述下问题:
    用户登陆时,可以选择是否保存cookie,以便以后直接访问,如果选择否,则在不活动指定时间后,将自动注销.
    Web.config中的设置如下:
    <authentication mode="Forms">
          <forms name=".SFCMS" protection="All" timeout="5" loginUrl="login.aspx" slidingExpiration="false" />
        </authentication>
    这里只解释一下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,自己设定票据的过期时间,测试通过.
  • 相关阅读:
    数据结构之 移位操作
    大话设计模式之外观模式
    JSP的内置对象(application)
    从键盘输入一个整数(1~20) 则以该数字为矩阵的大小,把1,2,3…n*n 的数字按照顺时针螺旋的形式填入其中。
    linux线程应用
    【网络挖掘:成就与未来方向】之网络挖掘应用程序与相关概念
    Thinking in Java之匿名内部类
    [Go] map
    [跟着hsp步步学习系统]oracle培训学习集锦全360度扫描(2)
    HDU3791:二叉搜索树
  • 原文地址:https://www.cnblogs.com/wuxilin/p/504720.html
Copyright © 2011-2022 走看看