网站开发中用户验证一般采用Asp.Net的Forms验证,验证票据存储到Cookie的方式。

    Session方式是将验证信息存储在内存中,如果你使用的虚拟主机给你分配很小的内存,实际上都是如此,那么session就会很快过期,要求你重新登录,如果用户正在填写信息,被要求重新登录,那愤怒的感觉可想而知。

   cookie是存储在用户的客户端的。但是也会碰到失效的问题,下面一一来了解。

    在ASP.NET Forms验证中,通常我们会使用ASP.NET自带的Login控件来进行验证。同时,在web.config文件中,我们所有的Forms设置都设为默认。现在,问题就来了。

1.为什么我明明点了"Remember me",而大概半个小时后,我又Log out了?

2.为什么我明明设置了timeout为无限期 e.g. 400000,为什么一两天之后我又Log out了呢?

这是Forms验证中遇到的比较多的问题。下面,我就这两个问题做一个详细的解释:对于问题一,首先我要阐明ticket和cookie的区别。 cookie是一个容器,用来存放东西,它是保存在客户端的。而ticket是具体的数据,用来表示具体的验证信息,它是放在cookie这个容器中的。 因而,在我们验证的过程中,以下事情发生了。首先,ticket被创造了,里面包含着用户名等信息,同时它有一个过期时间。

    然后,cookie被创造了,它同样也有一个过期时间。最后,将ticket保存在cookie中,并将此cookie发送到client的浏览器中。读 到这里,我想问题已经很明白了,用户的Log out就是因为时间过期的问题。但具体是谁的时间过期了呢?在我们ASP.NET web.config的设置中,timeout是cookie的过期时间(注意,默认是30分钟),而ticket的过期时间是无限的,因为我们选 了"Remember me".这就是为什么虽然我点了"Remember me"。

    但在30分钟左右后,我仍然被Log out了,因为我们并没有设置cookie的timeout.ticket和cookie,只要其中之一不是永远不过期,我们都无法实现永不过期。  

    当我们解决了问题一后(假如手动设置timeout="4000000"),我们又遇到了问题二。这又是什么原因呢?这得从ticket的加密解密机制说 起。ASP.NET会使用一个machinekey来对cookie进行加密,这个machinekey默认是在application启动时随机生成 的。然后,ASP.NET会使用同一个machinekey进行cookie进行解密。正式因为这个key是application启动时随机生成的才导 致了问题二。试想,如果application recycle(重启)了怎么办?

ASP.NET会生成另一个key进行解密,以前的cookie将不再有效,这就是问题二的原因了。知道了这个,解决第二个问题的办法就很简单了, 手动设置一个特定的key.如:<machineKey validationKey="88CB6CA6CF403C5FBB41C2F62BB7FCFCA05DE7BE" decryptionKey="B8A7CF3816C57176" validation="SHA1" />

实现Asp.net Forms身份验证的操作步骤

 

对于应用程序的身份验证,一直是自已编写登陆窗体,在窗体的CS文件中判断用户的登录 是否合法,如果合法则将用户名保存在Cookie中。然后将所有页面的继承于一个类似BaseForm这样的基页面,在这个页面的Page_Load事件 中加入判断,根据Cookie来判断用户是否已登录,如果没有登录则跳转到登录页面。

最近作一个互连网网站,想起安全性的问题,查阅了一些资料后觉得采用Asp.net提供的标准的Forms验证方式。研究了一下,现在写出操作步骤,以供以后使用的时候参考(博客现在的一个很重要的功能就是用来保存曾经的学习积累)

1、 修改web.config文件。如果vs2005的话,默认没有这个文件,调试时才会提示你是否要添加该文件。在web.config文件的<system.web>节中添加下面的三部分内容:

<authentication mode="Forms">
 
 
       <forms loginUrl="default.aspx" name=".ASPXFORMSAUTH">
 
 
       </forms>
 
 
     </authentication>
 
 
     <authorization>
 
 
       <deny users="?" />
 
 
     </authorization>
 
 
    <machineKey 
 
 
    validationKey="AutoGenerate,IsolateApps" 
 
 
    decryptionKey="AutoGenerate,IsolateApps"    
 
 
    validation="SHA1" 
 
 
    decryption="Auto" />

上面三部分分别标有不同颜色,第一部分是设定应用程序的身份验证模式,默认是 windows,如果你的系统是在局域网内使用,并且整个局域网是工作在域模式下,那windows身份验证方式将会有很好的效果,如果多个B/S用 windows模式来进行身份验证,那么他们甚至可以非常方便的就实现了单点登陆的效果。但Forms身份验证使用的更普遍些,虽然相对来说他存在安全性 差的问题。在authentication节下有个forms节,在那里你可以填上执行你身份验证的页面,以及存储身份验证所采用的Cookie名称。如 果你不填写Cookie名称的话,Asp.net默认分配.ASPXFORMSAUTH作为Cookie名称。

     第二部分authorization 是授权部分。你可以配置允许或拒绝某些用户或者角色来访问你的应用程序,他下面有deny,allow可以使用,搭配通配符?、*让我们可以非常轻易的配置出轻量级简单的身份验证体系。详细的配置信息可以参照asp.net的帮助文档,在此不在详细描述。

     第三部分 machineKey 是用来配置存取Cookie时采用的加密/解密算法。有了这个算法后,Cookie应该算是比较安全了吧。

2、 将Cookie信息登录后保存起来。

FormsAuthenticationTicket 
  new FormsAuthenticationTicket (1,CookieInfo , DateTime .Now, DateTime .Now.AddHours(20),false ,UserData ); // User data 
 
 
string encryptedTicket = FormsAuthentication .Encrypt(authTicket); // 
  加密 
 
 
//    
  存入Cookie 
 
 
HttpCookie authCookie = new HttpCookie (FormsAuthentication
 
 
         authCookie.Expires = authTicket.Expiration;
 
 
         Response.Cookies.Add(authCookie);

string FormsAuthentication 的时候会返回一个null值,以至于生成一个错误的加密Cookie值。如果要将Cookie进行长期保存,需要为Cookie设置一个过期时间。

3、 读取保存在计算机中的Cookie。

在Global.asax 文件中存在Application_AuthenticateRequest事件,它是执行所有服务器端请求的时候执行。因此我们可以在这个地方读取Cookie并进行解密。以下是示例代码:

protected 
  void Application_AuthenticateRequest(object sender, EventArgs
 
 
     {
 
 
string cookieName = FormsAuthentication .FormsCookieName;// 
  从验证票据获取Cookie的名字。
 
 
// 
  取得Cookie.
 
 
HttpCookie
 
 
if (null
 
 
return
 
 
FormsAuthenticationTicket authTicket = null
 
 
// 
  获取验证票据。
 
 
FormsAuthentication
 
 
if (null
 
 
return
 
 
       
 
 
// 
  验证票据的UserData中存放的是用户角色信息。
 
 
//UserData 
  本来存放用户自定义信息。此处用来存放用户角色。
 
 
string [] roles = authTicket.UserData.Split(new char [] { ','
 
 
FormsIdentity id = new FormsIdentity
 
 
GenericPrincipal principal = new GenericPrincipal
 
 
// 
  把生成的验证票信息和角色信息赋给当前用户.
 
 
         Context.User = principal;
 
 
     }

当你对Cookie加密存储之后必须要进行解密后才能进行使用。

的值。HttpContext .Current.User.Identity.Name

在项目中用到的验证(并没有实现cookis不过期):

public class  LoginUserManager : ILoginUserManager
     {
         private IUserService userService;

         public IUserService UserService
         {
             set { userService = value; }
         }

         public void MarkCurrentUser(User user, bool isPersistent)
         {
             string mUserData = string.Format("{0};{1};{2}", user.Account, user.UserName, user.EmployeeNO);
             SaveToCookies(user.Account, mUserData, isPersistent);
         }

         public User CurrentUser(bool fullData)
         {
             User member = GetFromCookies<User>();
             if (fullData && member != null && !string.IsNullOrEmpty(member.Account))
             {
                 return userService.GetObjectById(null, member.Account);
             }
             return member;
         }

         private static T GetFromCookies<T>() where T : class
         {
             if (HttpContext.Current.User != null && HttpContext.Current.User.Identity.IsAuthenticated
             && (HttpContext.Current.User.Identity is FormsIdentity))
             {
                 HttpCookie myCookie = HttpContext.Current.Request.Cookies["ud"];
                 if (myCookie != null && !string.IsNullOrEmpty(myCookie.Value))
                 {
                     FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(myCookie.Value);
                     if (ticket != null)
                     {
                         string[] userInfoArray = ticket.UserData.Split(';');
                         //UserData format like as "loginName;password;role1,role2,role3..."
                         if (userInfoArray.Length == 3 && typeof(T).Name == "User")
                         {
                             User member = new User();
                             member.Account = userInfoArray[0];
                             member.UserName = userInfoArray[1];
                             member.EmployeeNO = userInfoArray[2];
                             return member as T;
                         }
                     }
                 }
             }
             return default(T);
         }

         private static void SaveToCookies(string name, string mUserData, bool isPersistent)
         {
             FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
                 1,
                 name,
                 DateTime.Now,
                 DateTime.Now.AddDays(1),
                 isPersistent,
                 mUserData);
             string hashTicket = FormsAuthentication.Encrypt(ticket);
             HttpCookie cookie = new HttpCookie("ud", hashTicket);
             if (HttpContext.Current.Response.Cookies["ud"] != null)
             {
                 HttpContext.Current.Response.Cookies["ud"].Expires = DateTime.Now.AddDays(-1d);
             }
             HttpContext.Current.Response.Cookies.Add(cookie);
         }
     }