解決asp.net負載均衡時Session共享的問題
每個客戶端在訪問網站時,都會建立相應的Session,用來儲存客戶的狀態資訊,網站如果做了負載均衡,session共享是要做的,IIS對於session的儲存有五種模式
一、ASP.Net session儲存方式
1、InProc模式(程序內模式) 。為預設設定。
會話狀態儲存在Web伺服器上的記憶體中。
2、StateServer模式(狀態伺服器模式)。
會話狀態儲存在一個名為ASP.Net狀態服務的單獨程序中。這確保了在重新啟動Web應用程式時會保留會話狀態,並讓會話狀態可用於網路場中的多個Web伺服器。
3、SQL Server模式。
會話狀態儲存到一個SQL Server資料庫中。這確保了在重新啟動Web應用程式時會保留會話狀態,並讓會話狀態可用於網路場中的多個Web伺服器。
4、Custom模式
此模式允許您指定自定義儲存提供程式。
5、Off模式
此模式禁止會話狀態。
二、使用StateServer儲存session
如果網站做了負載均衡,對於session儲存就只能選擇2、3、4了,下面先來介紹一下StateServer模式,首先得開啟狀態服務
然後再對web站點的“會話狀態”進行設定
啟用本機的狀態服務
會自動在web.config裡生成配置檔案(如果不能生成就手動新增)
<sessionState mode=”StateServer” stateConnectionString=”tcpip=loopback:42424″ timeout=”20″ />
但是這裡就存在一個問題,如果每臺伺服器都照上面配置,各伺服器的Session都儲存在本機的StateServer裡面,還是沒有啟動共享的作用,這裡就需要讓一臺StateServer共享出來讓其他伺服器訪問,並將Session儲存到上面,執行regedit → 開啟登錄檔 → 找到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters節點 → 將 AllowRemoteConnection 的鍵值設定成“1”(1 為允許遠端電腦的連線,0 代表禁止)
也可以修改StateServer的埠
接下來將其它伺服器中web.config的配置檔案進行修改(sessionState指向開啟了允許遠端訪問的StateServer)
<sessionState mode=”StateServer” stateConnectionString=”tcpip=10.16.5.30:22222″ timeout=”20″ />
用StateServer這種共享式的session儲存方式不僅有安全隱患,而且像上面那臺共享的StateServer只要重啟伺服器,所有的session都會丟失,所以這種session儲存方式不是很完美,用StateServer儲存sesssion比較適合單機IIS開啟多程序的。
三、使用SQL server儲存session
要做保證安全並且不會因為重啟伺服器導致session丟失,那就要用sql server來儲存session,ASP.NET 2.0版本後微軟提供了aspnet_regsql.exe工具可以方便的配置Session資料庫.該工具位於 Web 伺服器上的系統根目錄Microsoft.NETFramework版本號資料夾中
cd C:\Windows\Microsoft.NET\Framework64\v4.0.30319 aspnet_regsql.exe -ssadd -sstype c -d <Database Name> -S <SQL Server IP> -U <User Name> -P <Password> aspnet_regsql.exe -ssadd -sstype c -d ASPState -S 10.16.5.36 -U sa -P HAha789
注:<Database Name>為資料庫名為ASPState ,<SQL Server IP>為資料庫例項名像 IBM-PC\SQLEXPRESS (若資料庫不是2005的不要寫ip地址,否則會連線失敗),<User Name>為sa(或與sa同等許可權的),<Password> 為 sa使用者名稱的密碼會話定義成功,但是會提示在web應用中進行相應的配置,此時檢視SQLServer會發現增加了資料庫ASPState,但是沒有表。
在命令列下執行如下命令:aspnet_regsql.exe -ssadd -sstype p -S <SQL Server IP> -U <User Name> -P <Password>
aspnet_regsql.exe -ssadd -sstype p -S 10.16.5.36 -U sa -P HAha789
該命令對此應用進行了持久化操作。這時會看到ASPState資料庫裡面多了兩張表,ASPStateTempSession就可以用來儲存Session,接下來要對web站點的“會話狀態”進行設定
ASPStateTempSessions 表中的SessionID ,包括兩個部分:網站生成的24位SessionID及8位AppId組成,對於不同的站點,其AppId和AppName也不同,在能夠在不同站點下Session共享,就得保證這個32位的SessionID 一致,所以可以通過修改儲存過程TempGetAppID,使其得到的SessionID與AppName無關,修改TempGetAppID如下
修改web.config(在資料庫中為ASPState單獨分配一個帳戶)
<sessionState mode="SQLServer" sqlConnectionString="data source=10.16.5.36;user id=sa;password=HAha789" cookieless="false" timeout="20"></sessionState>
這樣就實現了sql server對session的儲存,當然也可以用memcache來儲存session
四、ASP.NET錯誤,驗證檢視狀態MAC失敗
但在在網站登入訪問時卻報錯了“ASP.NET錯誤,驗證檢視狀態MAC失敗”,baidu了一下,大部分人都說是在頁裡或web.config里加EnableEventValidation="false" EnableViewStateMac="false" ViewStateEncryptionMode="Never" 這些屬性的設定。但是這並不從根本上解決問題,相反這樣做了反而更加不安全。不能說出錯就不用了?出錯得解決問題,得從根本上解決問題。
分析錯誤原因:
ASP.NET 中有很多涉及到加密的東西,比如 ViewState,比如 FormsAuthenticationTicket,這些東西都是要傳送到客戶端的,加密才能保障其安全性。加密就得有個私鑰,但這個私鑰我們並沒有指定啊,那是因為 ASP.NET 自動生成的。但是如果是在網路場或群集中,或者在某些做了 CDN 載入的虛擬主機中,由於涉及到多臺伺服器 ASP.NET 就無法為各臺機器自動生成相同的私鑰,這就造成了這個伺服器產生的資料,那臺伺服器解析不出來。於是就出錯了。怎麼辦?既然 ASP.NET 在多臺伺服器上無法自動隨機生成相同的私鑰,那只有我們自己指定了。
MachineKey生成工具,自動生成程式碼
https://www.fishlee.net/tools/machinekeygenerator
將生成的Machinekey插入到web.config中:
<system.web> <machineKey validationKey="A89B01ED6C356FCFEEB884EA523D7827F719C7A9F26BCC808A109C516D57DF91D2D7612127F06957C6643913DB893F66492A2FB85D44805735BA96DF41071B0F" decryptionKey="E9F179A7EF94763ABD9A79B07BA61F49550235125FADEF942BD2045B2A9803C8" validation="SHA1" decryption="AES" /> </system.web>
MachineKey的作用:
ASP.net 使用 forms authentication 時的 cookie 資料的加密和解密。以確保這部分資料不會被篡改viewstate 資料的加密和解密。以確保這部分資料不會被篡改。使用程序外session(out-of-process session)時,對會話狀態標識進行驗證。利用SessionStateMode的SQLServer來實現session共享,畢竟是微軟的東西,具有一定的侷限行,只能是sql server。其實session共享可以用其他的資料庫,比如memcache、redis
五、ASP.NET 狀態資料庫FAQ
1、如果把SESSION值存放到資料庫中去,使用者關閉了程式那怎麼樣清空資料庫裡的SESSION值呢?
實際ASP.NET在建立狀態資料庫的時候會在SQL Server代理(SQL Server Agent)的作業中新增一個作業,名稱為<狀態資料庫名>_Job_DeleteExpiredSessions。如果開啟SQL Server代理服務資料庫可以通過新增的狀態記錄的超時時間欄位(Exprires)定期對超時的狀態資料進行刪除。
2、ASPStateTempSessions表中的SessionId欄位如何使用?
資料庫中此表的SessionID欄位的值,由SessionID和AppID共同組成,最後8位為AppID所以,後8位之前一定是SessionID。例如,儲存在資料庫中的值為"ekr30c3mwvnc3145yrswew3a037e5e5a",後8位的"037e5e5a"為AppID,而前面的"ekr30c3mwvnc3145yrswew3a"為應用程式中你可以使用Session.SessionID獲得的字串。
3、如何判斷Session何時被更新的?
Session記錄被更新時會同時更新Expires和LockDateLocal,Expires欄位為UTC時間,如果想通過本地之間進行比較判斷還是需要使用LockDateLocal。
4、獲得Web.config配置檔案節點資訊的程式?
''獲得Web.config檔案配置例項
Dim configuration As System.Configuration.Configuration = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~/web.config")
''獲得狀態配置節點例項
Dim mSessionStateSection As System.Web.Configuration.SessionStateSection = CType(configuration.GetSection("system.web/sessionState"),System.Web.Configuration.SessionStateSection)
''獲得狀態模式
Response.Write(mSessionStateSection.Mode)
''獲得狀態超時時間
Response.Write(mSessionStateSection.Timeout)