1. ViewState的使用
ViewState的使用比较简单,主要是赋值和取值两步操作:
赋值:ViewState[key] = value;
取值:value = ViewState[key];
最主要的作用就是可以在当前页面保存值,ASP.NET的页面状态维护就是使用ViewState来实现的,基本上每一个ASPX页面都可以看到如下类似的html代码:
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMTkwNjc4NTIwMWRkyv4ncofW5vaWXdXRtXfXn3RYQR4="/>也就是说ViewState中的值实际上都是通过一个hidden(隐藏域)来保存的,hidden(隐藏域)的name为__VIEWSTATE,那么如果页面上有另外一个控件的名称也叫:__VIEWSTATE的话,会导致页面出错,因为同一个页面上不允许有两个相同ID的控件。其实在我们进行页面开发或者进行自定义控件开发的时候,都可以使用ViewState,用来保存当前页面的一些状态值,这样是非常方便的。ViewState是ASP.NET中特有的,相对于Session来说,它保存的值只能在当前页面使用,并且保存的只能是已经序列化的类,比如.NET中的strings, integers, Booleans, arrays, ArrayList, hashtable,DataTable,List<T>等。那么如何将自定义的类放入ViewState中呢,这个就涉及到序列化的问题了:序列化简单来说就是把一个对象转化成一种可以持久保存的数据,当下次需要使用时再把之前保存的数据反序列化成一个对象。当然在.NET中提供了简便的方法进行序列化的操作
下面我以一个简单的例子来说明:
3. ViewState的序列化与反序列化
PageStatePersister 是一个抽象类,是表示将ViewState信息序列化及反序列化机制的基类。在Page.LoadPageStateFromPersistenceMedium中示意代码如下:
protected internal virtual object LoadPageStateFromPersistenceMedium()
{
PageStatePersister pageStatePersister = this.PageStatePersister;
pageStatePersister.Load();
return new Pair(pageStatePersister.ControlState, pageStatePersister.ViewState);
}
在Page.SavePageStateToPersistenceMedium中的示意代码如下:
protected internal virtual void SavePageStateToPersistenceMedium(object state)
{
PageStatePersister pageStatePersister = this.PageStatePersister;
Pair pair = (Pair) state;
pageStatePersister.ControlState = pair.First;
pageStatePersister.ViewState = pair.Second;
pageStatePersister.Save();
}
在Asp.net2.0中实现PageStatePersister这个抽象类,具体提供持久化机制的类是HiddenFieldPageStatePersister。它实现了Load和Save两个方法,Load时将__VIEWSTATE反序列化为一个Pair对象,Save时将Pair对象序列化为一个字符串赋值给Page.ClientState。HiddenFieldPageStatePersister中采用的格式器是ObjectStateFormatter,其实现string Serialize(object state),和object Deserialize(string serializedState)这两个方法,从而实现对Pair对象的序列化和反序列化。
public string Serialize(object state)中的示意代码如下:
MemoryStream memoryStream = GetMemoryStream();
Serialize(memoryStream, state);
byte[] buf = memoryStream.GetBuffer();
if (RequiresViewStateEncryptionInternal)
{
buf = MachineKeySection.EncryptOrDecryptData(true, buf, this.GetMacKeyModifier(), 0, length);
length = buf.Length;
}
else if (EnableViewStateMac)
{
buf = MachineKeySection.GetEncodedData(buf, this.GetMacKeyModifier(), 0, ref length);
}
return Convert.ToBase64String(buf, 0, length);
将state序列化为内存流,在将其转换为字节流。如果需要加密则对其进行加密处理,否则需要Mac则进行Mac处理。最后将字节流进行Base64编码转换为字符串。
public object Deserialize(string serializedState) 中的示意代码如下:
byte[] buf = Convert.FromBase64String(serializedState);
int length = buf.Length;
if (ContainsEncryptedViewState)
{
buf = MachineKeySection.EncryptOrDecryptData(false, buf, this.GetMacKeyModifier(), 0, length);
length = buf.Length;
}
else if (EnableViewStateMac)
{
buf = MachineKeySection.GetDecodedData(buf, this.GetMacKeyModifier(), 0, length, ref length);
}
MemoryStream memoryStream = GetMemoryStream();
memoryStream.Write(buf, 0, length);
return this.Deserialize(memoryStream);
将字符串进行Base64解码为字节流,如果需要解密则进行解密处理,否则需要进行需要Mac则进行Mac处理,将字节流转换为内存流,进行反序列化返回Pair对象。
4. ViewState的优缺点:
(1)优点:
耗费的服务器资源较少(与Application、Session相比)。因为,视图状态数据都写入了客户端计算机中。
易于维护。默认情况下,DotNet系统自动启用对状态数据的维护。
因为它不使用服务器资源、不会超时,并且适用于任何浏览器。
(2)缺点:
性能问题。由于视图状态存储在页本身,因此如果存储较大的值,用户显示页和发送页时的速度仍然可能减慢。ViewState 增加了发送到浏览器的页面的大小,同时也增加了回传的窗体的大小,因此不适合存储大量数据。
设备限制。移动设备可能没有足够的内存容量来存储大量的视图状态数据。
潜在的安全风险。视图状态存储在页上的一个或多个隐藏域中。虽然视图状态以哈希格式存储数据,但它可以被篡改。如果直接查看页输出源,可以看到隐藏域中的信息,尽管 ViewState 数据已被编码,并且可以选择对其进行加密,但始终不将数据发送到客户端才是最安全的。
5. ViewState的安全性:
ViewState将一些信息保存在客户端,而且默认情况下客户端的数据仅仅是进行了Base64编码了,很容易被看到原文。当然ViewState还有一些安全性措施来改善这一点。ViewState 安全性对于处理和呈现 ASP.NET 页面所需的时间有直接的影响。简单地说,安全性越高,速度越慢。因此如果不需要,不要为 ViewState 添加安全性。
(1) MAC
MAC是message authentication check的简写即消息验证检查。MAC可以防止ViewState数据被篡改。可以通过设置EnableViewStateMAC="true"属性来启用消息验证检查。可以在页面级别上设置 EnableViewStateMAC,也可以在应用程序级别上设置。有一点需要特别说明的是在MSDN中说EnableViewStateMAC的默认值是Flase,但是实际上发现EnableViewStateMAC的默认值是True,这一点大家可以Page_Load中输出EnableViewStateMAC属性来验证。也就是说默认情况下都是对ViewState进行防篡改处理的。在ObjectStateFormatter中进行序列化的时候如果EnableViewStateMAC="true",则在MachineKeySection.GetEncodedData中根据字节流buf计算出一个20位的字节表示Hash值,增加在buf字节流的后边,形成一个新的字节流。
在ObjectStateFormatter中进行反序列化的时候如果EnableViewStateMAC="true",则在MachineKeySection.GetDecodedData中取buf的前(length-20)位计算出一个20位的字节表示Hash值,将这个Hash值与buf的最后20位进行比较,如果不一直则说明ViewState被篡改了则抛出异常。
默认情况下,.NET框架使用SHA1算法来生成ViewState散列代码。此外,也可以通过在machine.config文件中设置<machineKey>来选择 MD5 算法,如下所示:<machineKey validation="MD5" />。MD5算法的性能要比SHA1算法好一些,但是同样不够安全。
(2) ViewState加密
默认情况下__VIEWSTATE中存储的值仅仅仅进行了Base64编码,并没有进行加密,如果ViewState中有一些保密信息需要增加它的安全性,这样我们可以对ViewState进行加密。我们可以设置ViewStateEncryptionMode的值来决定是否加密,其值是“Auto”,“Always”,“Never”,默认值是“Auto”。“Always”表示进行加密,“Never”表示不进行加密,“Auto”时如果调用了RegisterRequiresViewStateEncryption方法后则进行加密。ViewStateEncryptionMode属性不能在代码中设置page指令或者配置文件中使用。
(3) 设置ViewStateUserKey
设置 ViewStateUserKey 属性有助于防止您的应用程序受到恶意用户的点击式攻击。必须在页处理的 Page_Init 阶段设置此属性。