非通用型的Web程式或產品,通常不會和ASP.NET中等安全模式打交道,因為面對的使用者群體會比較固定,或者部署環境是可以由程式提供者決定的。
但在做通用型的Web產品的時候,你就要和各種人打交道了,有的站長用的是國外空間,比如GoDaddy,外國的空間商通常會把ASP.NET代碼執行許可權控制在中等安全模式。
而在中等安全模式下,很多我們習以為常的事情都是做不了的。
中等安全模式是什嗎?
可能很多人都沒接觸過中等安全模式,我在參與bbsmax項目之前,我也不知道有中等安全模式這麼個東西。
簡單來說,ASP.NET提供了一個簡單設定代碼執行許可權的方案,叫做“信任層級”。
它預設提供5種信任層級,分別是:FullTrust、High、Medium、Low、Minimal。
每個信任層級的設定,分別對應於一組代碼使用權限設定。
這個方案,讓網站部署者可以通過web.config快速設定網站的Managed 程式碼執行許可權。
通過對web.config的<system.web>/<trust>節點的level屬性值進行設定,就可以將ASP.NET程式設定到不同的信任層級。
ASP.NET安裝完,所有網站預設都是FullTrust信任層級,也是最高信任層級。
本文說的“中等安全模式”就是對應於Medium信任層級。
因為Managed 程式碼執行許可權模型不是本文討論的重點,所以我這裡只做簡單的說明,不深入討論ASP.NET安全層級設定的實現原理,實現原理可以參考本文最後給出的幾個串連。
中等安全模式會有哪些影響?
以下是我和我的同事在ASP.NET中等安全模式下曾經遇到過的一些問題:
1. 基於VirtualPathProvider的模板機制不能用,因為VirtualPathProvider至少需要運行在High模式。
2. BuildProvider不能用,意味著你想自己添加自己的語言實現也不能用了,不過大部分項目不會用到這麼進階的東西。
3. CodeDom、Emit不能用了,這下慘了,什麼Ioc、AOP,動態注入的高科技玩意兒,全都廢了,這些不是基於CodeDom的就是基於Emit的。
4. 通過aspx頁面接管檔案下載也不行了,Response寫檔案流到用戶端需要更高的代碼執行許可權。
5. 大檔案上傳也別想了,因為大檔案上傳萬變不離HttpWorkRequst,擷取HttpWorkRequst的代碼需要FullTrust模式。
6. SQLite不能用了,因為中等安全模式下沒有Unmanaged 程式碼調用許可權,所以除了SQLite外,涉及到Unmanaged 程式碼調用的,也都廢掉了。
7. Access資料不能用OleDb串連了,因為中等安全模式下,OleDbClient是不能用的,你只能用ODBC資料來源。
所以,如果要考慮允許讓使用者把程式部署到中等安全模式下,那就越早做中等安全模式的相容性測試越好。
因為很多不能用的東西,都是涉及到基礎結構的。
比如,SQLite和Access不能用,你的程式如果正巧就只做這兩個資料庫的版本,咋辦?
比如,檔案下載不能通過Response寫檔案流的方式,你的程式正巧又是這麼做防盜鏈,咋辦?
中等安全模式要求那麼苛刻,怎麼對付?
只能繞道走了,要不然怎麼辦?方法還是有的,得不斷嘗試。
VirtualPathProvider不能用,BuildProvider也不能用,但是又需要有自己的一套模板文法。那隻好在頁面訪問前產生aspx頁面,再做URL重寫了。
說起來很簡單,就一句話。但是這中間我不知道寫了多少代碼,做了多少次實驗,才找到最佳方案。
所以,你的方法還是得你自己找。
以下是判斷程式是否運行在中等安全模式的代碼: 複製代碼 代碼如下:if (SecurityManager.IsGranted(new AspNetHostingPermission(AspNetHostingPermissionLevel.Medium)))
{
}
如果有遇到不是必須執行的邏輯,比如擷取程式記憶體佔用率或者大檔案上傳,那就可以先判斷下,再決定是否調用。
根據資料顯示,把程式集部署到GAC中,可以獲得FullTrust層級的許可權,不過我沒有實際試過。
附錄A
參考連結:
MSDN 《trust 元素(ASP.NET 設定架構)》
MSDN 《How To: Use Medium Trust in ASP.NET 2.0》
《Check Code Access Security Permissions Granted to your asp.net web application》
附錄B
辰 提供的檔案下載方案: 複製代碼 代碼如下:protected override void OnInit(EventArgs e)
{
Response.ContentType = "application/octet-stream";
using (FileStream stream = File.Open(Server.MapPath("~/test.txt"), FileMode.Open))
{
BinaryWriter writer = new BinaryWriter(Response.OutputStream);
byte[] buffer = new byte[1024];
int l = 0;
while ((l = stream.Read(buffer, 0, buffer.Length)) > 0)
{
writer.Write(buffer, 0, l);
}
}
}