惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Simon Willison's Weblog
Simon Willison's Weblog
G
Google Developers Blog
Spread Privacy
Spread Privacy
I
InfoQ
V
V2EX
S
Schneier on Security
小众软件
小众软件
C
CERT Recently Published Vulnerability Notes
博客园 - 聂微东
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Stack Overflow Blog
Stack Overflow Blog
T
Threat Research - Cisco Blogs
L
Lohrmann on Cybersecurity
Recent Announcements
Recent Announcements
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Attack and Defense Labs
Attack and Defense Labs
云风的 BLOG
云风的 BLOG
The Hacker News
The Hacker News
S
SegmentFault 最新的问题
C
Cybersecurity and Infrastructure Security Agency CISA
NISL@THU
NISL@THU
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
GbyAI
GbyAI
Latest news
Latest news
S
Secure Thoughts
Project Zero
Project Zero
MongoDB | Blog
MongoDB | Blog
I
Intezer
Security Latest
Security Latest
Apple Machine Learning Research
Apple Machine Learning Research
Vercel News
Vercel News
N
Netflix TechBlog - Medium
V2EX - 技术
V2EX - 技术
量子位
T
Threatpost
T
The Blog of Author Tim Ferriss
Y
Y Combinator Blog
T
Tor Project blog
A
Arctic Wolf
Microsoft Security Blog
Microsoft Security Blog
T
The Exploit Database - CXSecurity.com
大猫的无限游戏
大猫的无限游戏
T
Tailwind CSS Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
C
Check Point Blog
博客园 - Franky
Google DeepMind News
Google DeepMind News
The Register - Security
The Register - Security
The GitHub Blog
The GitHub Blog
L
LINUX DO - 热门话题

博客园 - wiseman

universal image loader在listview/gridview中滚动时重复加载图片的问题及解决方法 升级framework4.0后form认证票据失效的问题 vs2010下连接sql2000的一些问题 IIS ftp的一个问题:home directory inaccessible MS SQLServer2000 的孤立用户问题及解决. 服务器部署.NET 3.5的问题 愚蠢的错误 - wiseman - 博客园 VS2005项目迁移至VS2008初体验. Linux学习笔记:REHL AS4的上网配置,Http服务安装及配置,ftp服务的安装及配置 Linux学习笔记:需求和安装 通过了解MySpace的六次重构经历,来认识分布式系统到底该如何创建. WebService problem:The remote name could not be resolved 烦人的CommunityServer 简单的WEB程序压力测试. MS ASP.NET AJAX 1.0 Released! 用C#编写flash Application! Atlas提交中文时乱码的完整解决方案. 困扰了两天的问题... 一句C#代码的分析.
.NET2.0中Form验证的问题.
wiseman · 2006-09-15 · via 博客园 - wiseman

最近做一个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,自己设定票据的过期时间,测试通过.