ASP.NET Session詳解
當用戶在 Web 應用程序中導航 ASP.NET 頁時,ASP.NET 會話狀態使您能夠存儲和檢索用戶的值。HTTP 是一種無狀態協議。這意味著 Web 服務器會將針對頁面的每個 HTTP 請求作為獨立的請求進行處理。服務器不會保留以前的請求過程中所使用的變量值的任何信息。
ASP.NET 會話狀態將來自限定時間范圍內的同一瀏覽器的請求標識為一個會話,當每個用戶首次與這臺WWW服務器建立連接時,他就與這個服務器建立了一個Session,同時服務器會自動為其分配一個SessionID,用以標識這個用戶的唯一身份。Session提供用于在該會話持續期間內保留變量值的方法。默認情況下,將為所有 ASP.NET 應用程序啟用ASP.NET 會話狀態.
會話變量可以是任何有效的 .NET Framework 類型, 注意:當使用 InProc 以外的會話狀態模式時,會話變量類型必須為基元 .NET 類型或可序列化的類型。這是因為會話變量值存儲在外部數據存儲區中。
會話由一個唯一標識符標識,可使用 SessionID 屬性讀取此標識符。為 ASP.NET 應用程序啟用會話狀態時,將檢查應用程序中每個頁面請求是否有瀏覽器發送的 SessionID 值。如果未提供任何 SessionID 值,則 ASP.NET 將啟動一個新會話,并將該會話的 SessionID 值隨響應一起發送到瀏覽器。
默認情況下,SessionID 值存儲在 Cookie 中。但也可以將應用程序配置為在“無 Cookie”會話的 URL 中存儲 SessionID 值。只要一直使用相同的 SessionID 值來發送請求,會話就被視為活動的。如果特定會話的請求間隔超過指定的超時值(以分鐘為單位),則該會話被視為已過期。使用過期的 SessionID 值發送的請求將生成一個新的會話。
安全說明:
無論是作為 Cookie 還是作為 URL 的一部分,System.Web.SessionState.HttpSessionState.SessionID 值都以明文的形式發送。惡意用戶通過獲取 SessionID 值并將其包含在對服務器的請求中,可以訪問另一位用戶的會話。如果您將敏感信息存儲在會話狀態中,建議使用 SSL 來加密瀏覽器和服務器之間包含 SessionID 值的任何通信。
默認情況下,SessionID 值存儲在瀏覽器的不過期會話 Cookie 中。但是,通過在 Web.config 文件的 sessionState 節中將 cookieless 屬性設置為 true,可以指定不應將會話標識符存儲在 Cookie 中。
<configuration> <system.web> <sessionState cookieless="true" regenerateExpiredSessionId="true" /> </system.web> </configuration>
ASP.NET 通過自動在頁的 URL 中插入唯一的會話 ID 來保持無 Cookie 會話狀態。例如,下面的 URL 已被 ASP.NET 修改,以包含唯一的會話 ID lit3py55t21z5v55vlm25s55:
http://www.example.com/(S(lit3py55t21z5v55vlm25s55))/orderform.aspx
搭建Session服務器的幾種方式
將Session保存在獨立的服務器中可以實現在多臺Web服務器之間共享Session.雖然我們也可以自己開發Session存儲系統, 但是使用ASP.NET自帶的存儲機制將更加便捷.
.NET提供了5種保存Seission的方式:
方式名稱 | 存儲方式 | 性能 |
Off | 設置為不使用Session功能 | 無 |
InProc | 設置為將Session存儲在進程內,就是ASP中的存儲方式,這是默認值。 | 性能最高 |
StateServer | 設置為將Session存儲在獨立的狀態服務中。通常是aspnet_state.exe進程. | 性能損失10-15% |
SQLServer | 設置將Session存儲在SQL Server中。 | 性能損失10-20% |
Customer | 自定制的存儲方案 | 由實現方式確定 |
我們可以在Web.Config中配置程序使用的Session存儲方式.默認情況下是InProc, 即保存在IIS進程中. 關于Off, InProc和Customer本文不做講解. 相關文章大家都可以在網上搜索到.
下面主要講解 StateServer 和 SQLServer 的應用.
四.使用 StateServer 模式搭建Session服務器
(1)服務器端配置
1.啟動 Asp.net State service服務.(這個服務默認的狀態為手動.修改為自動并啟動.)
2.修改注冊表: [HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\aspnet_state\Parameters]
設置 AllowRemoteConnection = 1 , 設置 Port = 42424 (十進制,默認即為42424)
Port是服務的端口號
AllowRemoteConnection 表示是否允許其他機器連接,0為僅能本機使用,1為可以供其他機器使用.
(2)客戶端設置
在Web應用程序的Web.Config中, 我們需要修改 / 的節點.如果沒有
沒有則添加(默認使用的是InProc方式)
mode="StateServer"
stateConnectionString="tcpip=服務器ip:42424"
cookieless="false"
timeout="60"/>
上面的參數我們可以根據需要修改.
五.使用SqlServer模式搭建Session服務器
(1)服務器端配置
使用SqlServer模式搭建Session服務器端有兩種方式. ASP.NET 1.0和1.1版本請使用方式a, 2.0即以上版本請使用方式b.
a.使用SQL文件創建Session數據庫
在ASP.NET 1.0和1.1 版本中, 只能使用這種方式.對于2.0及其以上版本,請使用aspnet_regsql.exe工具.(當然此方法也通用2.0版本)
.net提供了數據庫安裝腳本,可以在機器的windows文件夾中找到:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ InstallSqlState.sql
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ InstallSqlStateTemplate.sql
根據ASP.NET的版本不同, 需要使用不同的SQL腳本. ASP.NET主要有1.1和2.0兩個版本,可以在不同的版本文件夾找到這兩個SQL.
InstallSqlState.sql 是創建默認名稱的數據庫"[ASPState]".此SQL可以直接運行.
InstallSqlStateTemplate.sql 可以使用自己指定的數據庫保存數據.此SQL需要自己修改后運行, 打開SQL文件將其中 [DatabaseNamePlaceHolder] 替換為自己指定的數據庫名稱.
執行installsqlstate.sql時不需要指定數據庫,可以在任意數據庫上執行.此SQL會自己創建新的數據庫
b. 使用aspnet_regsql.exe工具
ASP.NET 2.0版本后微軟提供了aspnet_regsql.exe工具可以方便的配置Session數據庫.該工具位于 Web 服務器上的"系統根目錄\Microsoft.NET\Framework\版本號"文件夾中.
使用舉例:
aspnet_regsql.exe -S . -U sa -P 123456 -ssadd -sstype p
-S參數:
表示數據庫實例名稱. 可以用"."表示本機.
-U和-P參數:
表示用戶名和密碼.
-E參數:
可以再-U –P 與 -E中選擇一組. –E表示以當前系統用戶通過windows身份驗證登錄數據庫, -U -P則是使用SqlServer用戶登錄數據庫.
-ssadd / –ssremove 參數:
-ssadd表示是添加Session數據庫, -ssremove表示移除Session數據庫.
sstype 參數:
選項 | 說明 |
t | 將會話數據存儲到 SQL Server tempdb 數據庫中。這是默認設置。如果將會話數據存儲到 tempdb 數據庫中,則在重新啟動 SQL Server 時將丟失會話數據。 |
p | 將會話數據存儲到 ASPState 數據庫中,而不是存儲到 tempdb 數據庫中。 |
c | 將會話數據存儲到自定義數據庫中。如果指定 c 選項,則還必須使用 -d 選項包括自定義數據庫的名稱。 |
(2)Session客戶端設置
此房是同樣需要Web應用程序修改Web.Config中的節點.如果使用默認的數據庫(ASPState庫), 則配置如下:
<sessionState mode="SQLServer" sqlConnectionString="server=192.168.9.151; uid=sa; pwd=123456;" />
如果使用了自定義的數據庫名稱,則還需要制定allowCustomSqlDatabase屬性并在數據庫連接串中指定數據庫:
<sessionState mode="SQLServer" allowCustomSqlDatabase="true" sqlConnectionString="server=192.168.9.151; DataBase=MyAspState;uid=sa; pwd
=123456;" />
六.使用經驗與技巧總結
下面是SessionID, Session_End時間, StatServer模式 和 SqlServer模式的各種經驗和技巧總結.
(1)StateServer模式:
1.在web farm中,請確認在所有的web服務器上有相同的
2. 要保存在Session中的對象是可序列化的。
3.為了在web farm中的不同web服務器上維護session state,IIS Metabase中的網站應用程序路徑(如\LM\W3SVC\2)應該在所有的服務器上保持一致(大小寫敏感).
4. ASP.NET處理Session是在Machine.Config中配置的HttpModuel模塊, 在.NET的安裝目錄下的Config文件夾中, 查看Web.Config(1.1版本是在Machine.Config):
確認此模塊是否存在.
5.StateServer不支持負載均衡, 所以如果大并發推薦使用SqlServer模式, 可以享受到SqlServer的高性能和安全性.雖然存儲效率會有下降.
6.需要讓所有機器的MachineKey相同.在Machine.Config中配置:
<machineKey validationKey="1234567890123456789012345678901234567890AAAAAAAAAA" decryptionKey="123456789012345678901234567890123456789012345678" validation="SHA1" decryption="Auto"/>
(2)SqlServer模式:
1. 要保存在Session中的對象是可序列化的。
2. 如果使用了默認是數據庫, 則在客戶端配置文件中的數據庫鏈接字符串的用戶,需要擁有ASPState和tempdb兩個庫的dbowner權限.
3. 在SQLServer模式下,session過期是由SQL Agent使用一個注冊任務完成的,要確認SQL Agent已經運行。否則無法清理過期的Session數據, 會導致數據庫數據一直增加.
4. 如果使用SqlServer模式時, 對于Web場中的各服務器的 ASP.NET 應用程序路徑必須是相同的。請在 IIS 配置數據庫中對 Web 場中的所有 Web 服務器進行 Web 站點的應用程序路徑同步。大小寫一定要相同,因為 Web 站點的應用程序路徑是區分大小寫的。
5.需要讓所有機器的MachineKey相同.在Machine.Config中配置:
<machineKey validationKey="1234567890123456789012345678901234567890AAAAAAAAAA" decryptionKey="123456789012345678901234567890123456789012345678" validation="SHA1" decryption="Auto" />
(3)Session:
1. 不能直接通過Session服務器在ASP.NET和ASP之間共享Session. 請使用微軟提供的解決方案:
http://msdn.microsoft.com/zh-cn/library/aa479313.aspx
2. 在不同的應用程序或一個網站的不同虛擬目錄之間無法共享Session
3. Session的過期時間是滑動時間.
4. Session存儲.NET自帶的值類型性能最優. 存儲對象會降低性能.
(4)SessionID:
1.SessionID 還可以保存在URL上, 設置Web.Config文件中的System.Web/sessionState節點的Cookiesless屬性即可:
2. 一般在Session超時或刪除之后,SessionID保持不變. 因為Session過期后會在服務器端清除數據, 但是SessionID保存在用戶瀏覽器上, 所以只要瀏覽器不關閉則HTTP頭中的SessionID保持不變.
3.關閉瀏覽器后再訪問, SessionID會不同.
4.每打開一個IE6窗口, SessionID都不同, 在IE6中兩個窗口的Session不能共享.
5.FireFox的標簽頁和新的FireFox窗口, SessionID都相同, 在FF的窗口和標簽頁上Session能共享.
6.對于包含FrameSet的頁面,比如:
如果后綴名是.htm并且.htm文件沒有交給ASP.NET的ISAPI處理, 那么根據服務器速度在每個Frame頁面生成不同的SessionID, 再刷新后相同都等于最后一個SessionID.
解決辦法是將.htm后綴改成.aspx, 或者將.htm文件交給ASP.NET的ISAPI處理.
(5)Session_End事件:
1. Session_End僅在InProc模式中可用
2. 關閉瀏覽器,Session_End是不會觸發的。HTTP是一種無狀態協議,服務器沒有辦法知道你的瀏覽器是否已經關閉。
3. 當Session因為時間過期或調用Session.Abandon時,Session_End才會觸發.Session.Clear()僅僅是清除數據,但沒有刪除session。
4. Session_End由一個后臺線程觸發,使用工作者進程賬號運行. 所以程序不會通知發生的錯誤.
5. 在Session_End訪問數據庫要考慮權限問題. Session_End是用運行工作者進程(aspnet_wp.exe)的帳號運行的,這個賬號可以在machine.config中指定。因此,在Session_End中,如果使用integrity security連接SQL,它將使用工作者進程賬號身份連接,這可能會引起登錄失敗.
6.因為Session_End是有獨立線程出發的, 所以在Session_End中無法使用HttpContext對象(Request,Response,Server等對象都在HttpContext中), 即無法使用 Response.Redirect 和Server.Transfer等方法.
七.總結
我已經使用SqlServer模式對公司的多臺服務器實現了Session共享, 服務器重啟也不會導致用戶預定過程重新開始(預定過程需要的Session不會丟失). 希望本文對具體的Session服務器搭建人員有所幫助.