• 维护应用程序状态(一):使用浏览器cookie


    那些刚刚进行Web编程的开发人员,总是难以理解维护应用程序状态的问题。万维网最基础的协议HTTP协议是一个无状态协议,也就是说,从Web服务器端的角度来看,每一个访问请求都是一个新的用户。HTTP协议不提供任何方法来决定两个访问请求是否是同一个用户发出的。

    然而,维护状态对任何Web应用程序来说都是非常重要的。购物车是一个经典的例子。如果希望在多个页面请求之间关联一个购物车和某个用户,就需要一些维护状态的方法。

    这一系列关注三种包含在ASP.NET 2.0 Framework中的、在多个页面请求间关联数据和特定的用户的方法。在第一部分,你将了解到如何创建和处理浏览器cookie。浏览器cookie用于关联一些文本到网站的每一个用户。

    接着,你将了解如何使用会话状态(session state)。会话状态用于关联任意类型的对象到任何用户。例如,可以存储购物车对象到会话状态中。

    你将了解到如何使用不依赖cookie的会话状态,这样,即使浏览器禁用了cookie,你也能使用会话状态。你还将了解到如何通过开启进程外会话状态,从而让会话状态更健壮(robust)。

    最后,我们介绍ASP.NET 2.0 Framework引入的一个新特性:profile对象。profile对象提供一个创建强类型和持久化方式的会话状态的方法。

    你将了解到定义用户配置文件的不同方法,以及如何在一个组件内使用profile对象。最后,你将了解如何实现一个自定义profile提供程序。

     

    1.1  使用浏览器cookie

    cookie是由第一个版本的Netscape浏览器引入。Netscape的开发人员发明了cookie来解决当时折磨互联网的一个问题,那就是没办法赚钱,因为没办法创建一个购物车。

    注解:可以从http://home.netscape.com/newsref/std/cookie_spec.html 阅读Netscape的原始cookie定义。

    先讲讲cookie是如何工作的。当Web服务器端创建一个cookie时,一个附加的HTTP首部在浏览器显示页面时被发送到浏览器。HTTP首部类似如下形式:

    Set-Cookie: message=Hello

    Set-Cookie首部使得浏览器创建一个名为message的cookie,包含值Hello。

    在浏览器创建cookie后,它从相同的应用程序请求页面时,都将像下面这样发送这个HTTP首部:

    Cookie:message=Hello

    cookie头包含所有的Web服务器端设置的cookie。每次浏览器向Web服务器端请求页面时,cookie都会发送回服务器端。

    注意,一个cookie就是一段文本。cookie只能用于存储字符串值。

    实际上,我们可以存储两种类型的cookie:会话cookie和持久化cookie。一个会话cookie只存在于内存中。当用户关闭浏览器时,会话cookie就永远消失了。

    而持久化cookie可以存在几个月甚至几年。当创建一个持久化cookie时,cookie被浏览器长久存在用户的电脑上。以IE为例,cookie以一组文本文件的形式保存在下面的文件夹:

    \Documents and Settings\[user]\Cookies

    另一方面,对于Mozilla Firefox浏览器则将cookie保存在下面的文件:

    \Documents and Settings\[user]\Application Data\Mozilla\Firefox\Profiles\➥[random folder

    *name]\Cookies.txt

    因为不同的浏览器将cookie存于不同的位置,cookie是浏览器独立的。使用IE浏览器请求页面并创建的cookie,不存在于Firefox或Opera浏览器中。

    此外,应注意IE和Firefox浏览器都以明文形式存储cookie。我们不应该将诸如社会保险号,或信用卡号码之类的敏感信息存于cookie中。

    注解   cookie这个名称从哪里来?根据Netscape最初的cookie文档,cookie并不是因为什么特别的原因被选中的。然而,这个名称最有可能继承自UNIX世界,类似“magic cookie”,在程序间传递的一种不透明的令牌。

    1.1.1  cookie的安全性限制

    cookie会涉及安全方面的问题。当创建一个持久化cookie时,我们会修改访问者电脑上的文件。有些人就是一天到晚梦想着对你的电脑做些坏事。为了避免电脑遭到袭击,浏览器对cookie有一些强制的安全限制。

    首先,所有的cookie是域名独立的。Amazon网站设置的cookie,巴诺书店网站访问不到。浏览器创建一个cookie时,会记录关联到cookie的域名,不会将其发送到另一个域名。

    注解   一个包含在Web页面的图片可能来自和页面不同的另一个域名。当浏览器请求一个图片时,cookie可能被另一个域名设置。诸如DoubleClick这样的公司,就利用这样的后门在多个不同的页面间跟踪并显示广告统计信息。这种类型的cookie就称为第三方cookie。

    另一个浏览器存储cookie的重要限制是其大小的限制。一个域名存储的cookie总大小不能超过4 096byte。这个大小限制,包含所有的cookie名称和值在内。

    注解:IE5.0以上版本支持一个名叫userData行为的功能。userData行为允许存储远大于cookie的数据(对局域网10 240KB,对因特网站点1 024KB)。要了解更多关于userData行为的信息,请访问微软MSDN网站(msdn.microsoft.com)。

    最后,大多数浏览器都限制可以被设置的cookie数量,一个域名不超过20个cookie(不包括IE浏览器)。超过这个数目,旧的cookie会被自动删除。

    注解:美国白宫管理和财政办公室阻止所有的联邦网站创建持久化cookie,除非“强制需要”。见http://www.whitehouse.gov/omb/memoranda/m00-13.html
    美国国家安全应急中心网站(www.nsa.gov)最近就遇到了创建持久化cookie的问题。他们在收到来自隐私倡议的阻止警告后停止了使用持久化cookie。
    个人认为这样的cookie偏执策略有点疯狂,不过我们还是要注意它。

    因为所有关联到cookie的安全性方面的因素,所有的现代浏览器给用户提供了禁用cookie的选项。这意味着,除非在构建局域网应用程序时我们控制每个人的浏览器,其他时候的默认情况下,不应该企图依赖cookie。我们应尽量只在存储非关键信息时使用cookie。

    据说,ASP.NET Framework的许多部分依赖cookie。例如,Web部件、表单验证、会话状态和匿名用户配置文件默认情况下都依赖cookie。如果你使用这些功能中的任何一个,就必须使用cookie。

    并且,许多网站也依赖cookie。Yahoo!和MSDN网站的许多网页如果没有开启cookie就访问不了。换句话说,需要访问者开启cookie才能访问我们的网站,并不完全是不合理的要求。

    1.1.2  创建cookie

    可以通过给Response.Cookies集合添加cookie来创建新的cookie。Response.Cookies集合包含所有Web服务器端发送到Web浏览器的cookie。

    例如,代码清单1-1所示的页面将创建一个新的名叫Message的cookie。该页面包含一个输入Message这个cookie的值的表单(见图1-1)。

    1-1  创建cookie

    代码清单1-1  SetCookie.aspx

    <%@ Page Language="C#" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 

    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
    >

    <script runat="server">

        protected void btnAdd_Click(object sender, EventArgs e)

        {

            Response.Cookies["message"].Value = txtCookieValue.Text;

        }

    </script>

    <html xmlns="http://www.w3.org/1999/xhtml" >

    <head id="Head1" runat="server">

        
    <title>Set Cookie</title>

    </head>

    <body>

        
    <form id="form1" runat="server">

        
    <div>

        

        
    <asp:Label

            
    id="lblCookieValue"

            Text
    ="Cookie Value:"

            AssociatedControlID
    ="txtCookieValue"

            Runat
    ="server" />

        
    <asp:TextBox

            
    id="txtCookieValue"

            Runat
    ="server" />

        
    <asp:Button

            
    id="btnAdd"

            Text
    ="Add Value"

            OnClick
    ="btnAdd_Click" 

            Runat
    ="server" />

            

        
    </div>

        
    </form>

    </body>

    </html>

    注意,cookie的名称是大小写敏感的。设置一个名叫message的cookie和设置一个名叫Message的cookie是不同的。

    如果希望修改代码清单1-1所示的页面创建的cookie,可以打开页面,并给Message这个cookie输入一个新的值。当Web服务器端将它发送到浏览器时,修改的cookie值就会被设置到浏览器。

    代码清单1-1所示的页面创建的是一个会话cookie(session cookie)。当关闭Web浏览器时,该cookie就消失了。如果希望创建持久化cookie(persistent cookie),则需要为cookie指定一个过期时间。

    代码清单1-2所示的页面创建了一个持久化cookie。

    代码清单1-2  SetPersistentCookie.aspx

    <%@ Page Language="C#" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 

    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
    >

    <script runat="server">

        void Page_Load()

        {

            // Get current value of cookie

            int counter = 0;

            if (Request.Cookies["counter"] != null)

                counter = Int32.Parse(Request.Cookies["counter"].Value);

            

            // Increment counter

            counter++;

            

            // Add persistent cookie to browser

            Response.Cookies["counter"].Value = counter.ToString();

            Response.Cookies["counter"].Expires = DateTime.Now.AddYears(2);

            

            // Display value of counter cookie

            lblCounter.Text = counter.ToString();

        }

    </script>

    <html xmlns="http://www.w3.org/1999/xhtml" >

    <head id="Head1" runat="server">

        
    <title>Set Persistent Cookie</title>

    </head>

    <body>

        
    <form id="form1" runat="server">

        
    <div>

        

        You have visited this page

        
    <asp:Label  

            
    id="lblCounter"

            Runat
    ="server" />

        times!

        

        
    </div>

        
    </form>

    </body>

    </html>

    代码清单1-2所示的页面记录页面被请求的次数。一个名叫counter的持久化cookie被用来记录页面被请求的次数。注意,counter的过期时间被设为2年以后。当为某个cookie设置了过期时间后,cookie就被保存为持久化cookie了。

     

    1.1.3  读取cookie

    可以使用Response.Cookies集合创建和修改cookie,也可以使用Request.Cookies集合读取cookie值。

    例如,代码清单1-3所示的页面读取message cookie的值。

    代码清单1-3  GetCookie.aspx

    <%@ Page Language="C#" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 

    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
    >

    <script runat="server">

        void Page_Load()

        {

            if (Request.Cookies["message"] != null)

                lblCookieValue.Text = Request.Cookies["message"].Value;

        }

        

    </script>

    <html xmlns="http://www.w3.org/1999/xhtml" >

    <head id="Head1" runat="server">

        
    <title>Get Cookie</title>

    </head>

    <body>

        
    <form id="form1" runat="server">

        
    <div>

        

        The value of the message cookie is:

        
    <asp:Label

            
    id="lblCookieValue"

            Runat
    ="server" />

        

        
    </div>

        
    </form>

    </body>

    </html>

        在代码清单1-3中,IsNothing()函数用来在读取cookie值之前检查cookie是否存在。如果不包含该检查,就有可能获得一个空引用异常。同样,不要忘记cookie的名称是大小写敏感的。

    代码清单1-4所示的页面列出包含在Request.Cookies集合中的所有cookie(见图1-2)。

    1-2  显示所有cookie列表

    代码清单1-4  GetAllCookies.aspx

    <%@ Page Language="C#" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 

    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
    >

    <script runat="server">

        void Page_Load()

        {

            ArrayList colCookies = new ArrayList();

            for (int i = 0; i 
    < Request.Cookies.Count; i++)

                colCookies.Add(Request.Cookies[i]);

            grdCookies.DataSource 
    = colCookies;

            
    grdCookies.DataBind();

        }

    </script
    >

    <html xmlns="http://www.w3.org/1999/xhtml" >

    <head id="Head1" runat="server">

        
    <title>Get All Cookies</title>

    </head>

    <body>

        
    <form id="form1" runat="server">

        
    <div>

        

        
    <asp:GridView

            
    id="grdCookies"

            Runat
    ="server"/>

            

        
    </div>

        
    </form>

    </body>

    </html>

    注意,从Request.Cookies集合迭代获得的有意义的信息只有HasKey,Name和Value属性。其他列都显示了不正确的信息。例如,Expires列总是显示最小日期。浏览器不与页面请求交互这些额外的属性,所以也就获取不了这些属性的值。

    使用Request.Cookies集合时,非常重要的一点是,理解For...Each循环返回的值和For...Next循环返回的值不同。如果用For...Each循环迭代Request.Cookies集合,返回的是cookies的名称。如果用For...Next循环迭代该集合,返回的是HeepCookie类(下一节介绍)的示例。

     

    1.1.4  设置cookie属性

    HttpCookie类代表cookie。当创建或读取一个cookie时,可以使用该类的下面这些属性:

    q Domain——用于设置关联到cookie的域名,默认值是当前域名;

    q Expires——用于通过给定一个过期时间创建一个持久化cookie;

    q HasKeys——用于指定该cookie是否是一个多值cookie(见本章稍后的3.1.6节);

    q HttpOnly——用于避免cookie被JavaScript访问;

    q Name——用户指定cookie的名称;

    q Path——用于指定关联到cookie的路径。默认值为/;

    q Secure——用于指定cookie需要通过安全Socket层(SSL)连接传递;

    q Value——允许读/写cookie的值;

    q Values——当使用多值cookie时,用于读/写特定的值(见本章稍后的3.1.6节)。

    这些属性中的一部分需要更多的解释。例如,你可能发现Domain属性有些奇怪,因为你修改不了关联到cookie的domain。

    Domain属性对于组织子域名时会非常有用。如果需要设置cookie可以被Sales.MyCompany.com,Managers.MyCompany.com和Support.MyCompany.com访问,则需要设置Domain属性值为.MyCompany.com(注意开头的部分),而不能使用该属性关联cookie到一个完全不同的域名。

    HttpOnly属性用户设置一个cookie是否可以通过JavaScript访问。该属性只对IE6(SP1)级以上版本有效。引入该属性是为了防止跨站点脚本攻击。

    Path属性用于限定cookie到一个特定的路径。例如,如果在相同的域名部署多个应用程序,而不希望应用程序共享相同的cookie,则需要设置Path属性避免一个应用程序读取另一个应用程序的cookie。

    Path属性听起来很有用。不幸的是,你不应该使用它。IE对于路径是大小写敏感的。如果用户在地址栏输入一个不同大小写的路径,则cookie不会被发送。换句话说,下面两个路径并不匹配:

    http://localhost/original/GetAllCookies.aspx

    http://localhost/ORIGINAL/GetAllCookies.aspx

    1.1.5  删除cookie

    删除一个cookie的方法并不直观。要删除一个存在的cookie,必须设置其过期时间为一个过去的时间。

    代码清单1-5所示的页面演示了如何删除一个单值cookie。页面包含一个用于输入cookie名称的表单。当提交表单时,指定名称的cookie就被删除了。

    代码清单1-5  DeleteCookie.aspx

    <%@ Page Language="C#" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 

    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
    >

    <script runat="server">

        protected void btnDelete_Click(object sender, EventArgs e)

        {

            Response.Cookies[txtCookieName.Text].Expires = DateTime.Now.AddDays(-1);

        }

    </script>

    <html xmlns="http://www.w3.org/1999/xhtml" >

    <head id="Head1" runat="server">

        
    <title>Delete Cookie</title>

    </head>

    <body>

        
    <form id="form1" runat="server">

        
    <div>

        

        
    <asp:Label

            
    id="lblCookieName"

            Text
    ="Cookie Name:"

            AssociatedControlID
    ="txtCookieName"

            Runat
    ="server" />

        
    <asp:TextBox

            
    id="txtCookieName"

            Runat
    ="server" />

        
    <asp:Button

            
    id="btnDelete"

            Text
    ="Delete Cookie"

            OnClick
    ="btnDelete_Click" 

            Runat
    ="server" />

            

        
    </div>

        
    </form>

    </body>

    </html>

    当需要删除cookie时设置的特定的日期只要是一个过去的值,无所谓实际的值是多少。在代码清单1-5中,过期时间被设为一天前。

    代码清单1-6所示的页面从浏览器删除当前域名(和路径)下的所有cookie。

    代码清单1-6  DeleteAllCookies.aspx

    <%@ Page Language="C#" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 

    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
    >

    <script runat="server">

        

        void Page_Load()

        {

            string[] cookies = Request.Cookies.AllKeys;

            foreach (string cookie in cookies)

            {

                BulletedList1.Items.Add("Deleting " + cookie);

                Response.Cookies[cookie].Expires = DateTime.Now.AddDays(-1);

            }

        }

    </script>

    <html xmlns="http://www.w3.org/1999/xhtml" >

    <head id="Head1" runat="server">

        
    <title>Delete All Cookies</title>

    </head>

    <body>

        
    <form id="form1" runat="server">

        
    <div>

        

        
    <h1>Delete All Cookies</h1>

        

        
    <asp:BulletedList

            
    id="BulletedList1"

            EnableViewState
    ="false"

            Runat
    ="server" />

        

        
    </div>

        
    </form>

    </body>

    </html>

    代码清单1-6所示的页面从Request.Cookies集合中循环遍历并删除所有的cookie。

     

    1.1.6  使用多值cookie

    根据cookie规范,对单个域名,浏览器不能存储超过20个cookie。可以通过创建多值cookie来超越该限制。多值cookie是一个包含子键的单一cookie。可以根据需要创建任意数量的子键。

    例如,代码清单1-7所示的页面创建了一个名叫preferences的多值cookie。preferences cookie用于存储first name(名)、last name(姓)和favorite color(喜爱的色彩)(见图1-3)。

    1-3  创建多值cookie

    代码清单1-7  SetCookieValues.aspx

    <%@ Page Language="C#" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 

    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
    >

    <script runat="server">

        void btnSubmit_Click(Object s, EventArgs e)

        {

            Response.Cookies["preferences"]["firstName"] = txtFirstName.Text;

            Response.Cookies["preferences"]["lastName"] = txtLastName.Text;

            Response.Cookies["preferences"]["favoriteColor"] = txtFavoriteColor.Text;

            Response.Cookies["preferences"].Expires = DateTime.MaxValue;

        }

    </script>

    <html xmlns="http://www.w3.org/1999/xhtml" >

    <head id="Head1" runat="server">

        
    <title>Set Cookie Values</title>

    </head>

    <body>

        
    <form id="form1" runat="server">

        
    <div>

        

        
    <asp:Label

            
    id="lblFirstName"

            Text
    ="First Name:"

            AssociatedControlID
    ="txtFirstName"

            Runat
    ="server" />

        
    <br />    

        
    <asp:TextBox

            
    id="txtFirstName"

            Runat
    ="server" />

        
    <br /><br />

        
    <asp:Label

            
    id="lblLastName"

            Text
    ="Last Name:"

            AssociatedControlID
    ="txtFirstName"

            Runat
    ="server" />

        
    <br />    

        
    <asp:TextBox

            
    id="txtLastName"

            Runat
    ="server" />

        
    <br /><br />

        
    <asp:Label

            
    id="lblFavoriteColor"

            Text
    ="Favorite Color:"

            AssociatedControlID
    ="txtFavoriteColor"

            Runat
    ="server" />

        
    <br />    

        
    <asp:TextBox

            
    id="txtFavoriteColor"

            Runat
    ="server" />

        
    <br /><br />

        
    <asp:Button

            
    id="btnSubmit"

            Text
    ="Submit"

            OnClick
    ="btnSubmit_Click"

            Runat
    ="server" />

        

        

        
    </div>

        
    </form>

    </body>

    </html>

    当提交代码清单1-7所示的页面时,下面的HTTP首部被发送到浏览器:

    Set-Cookie: preferences=firstName=Steve&lastName=Walther&favoriteColor=green;

    expires=Fri, 31-Dec-9999 23:59:59 GMT; path=/

    代码清单1-8所示的页面从preferences cookie读取值。

    代码清单1-8  GetCookieValues.aspx

    <%@ Page Language="C#" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 

    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
    >

    <script runat="server">

        void Page_Load()

        {

            if (Request.Cookies["preferences"] != null)

            {

                lblFirstName.Text = Request.Cookies["preferences"]["firstName"];

                lblLastName.Text = Request.Cookies["preferences"]["lastName"];

                lblFavoriteColor.Text = Request.Cookies["preferences"]["favoriteColor"];

            }

        }

    </script>

    <html xmlns="http://www.w3.org/1999/xhtml" >

    <head id="Head1" runat="server">

        
    <title>Get Cookie Values</title>

    </head>

    <body>

        
    <form id="form1" runat="server">

        
    <div>

        

        First Name:

        
    <asp:Label

            
    id="lblFirstName"

            Runat
    ="server" />

        
    <br />

        Last Name:

        
    <asp:Label

            
    id="lblLastName"

            Runat
    ="server" />

        
    <br />

        Favorite Color:

        
    <asp:Label

            
    id="lblFavoriteColor"

            Runat
    ="server" />

        

        
    </div>

        
    </form>

    </body>

    </html>

    可以使用HttpCookie.HasKey属性判断一个cookie是一个普通cookie还是一个多值cookie。

  • 相关阅读:
    IPC(进程间通信)
    进程和线程是什么
    虚拟内存
    寄存器是什么?内存、寄存器和存储器的区别
    计算机资源 —硬件资源分配
    如何将一个网页中自己想要的数据导入到Excel表格中
    Putty的安装和使用
    SQL中的ON DUPLICATE KEY UPDATE使用详解
    sql:主键(primary key)和唯一索引(unique index)区别
    直接扩频通信(上)理论基础
  • 原文地址:https://www.cnblogs.com/greatandforever/p/1619224.html
Copyright © 2020-2023  润新知