摘要:華能集團下某發電廠的企業網站(基於Asp.Net2.0實現,不允許修改來源程式)要求實現“廠內使用者可直接存取整個網站的所有頁面,廠外使用者只能訪問指定的頁面”的功能,本文將按照需求分析、方案設計、編碼實現、部署應用的順序逐步闡述整個解決方案的形成過程。
1. 需求分析
通過深入的交流和溝通,確認了該發電廠在企業網站使用者存取控制方面的改進要求,大致情況如下:
a) 網站基於Asp.Net2.0實現,不允許修改來源程式
b) 廠內使用者可直接存取整個網站的所有頁面,員工不需要身分識別驗證
c) 廠外使用者只能訪問指定的頁面
顯而易見,他們就是針對企業網站增加一項IP過濾功能,在廠外使用者訪問某些敏感頁面時將其拒之門外。首先我們需要設定一個IP列表和一個Url列表,前者包含所有廠內IP,後者包含廠外使用者可訪問的全部Url,並且這兩個列表都是可維護的;另外一個核心問題是,我們需要選擇一個合理的方式將開發好的功能模組整合到企業網站中,HttpModules義不容辭。
在Asp.Net時代,IIS 接收到請求並將其調度給 aspnet_isapi.dll之後,ASP.NET 引擎開始逐個對已配置的HTTP模組(HttpModules)進行初始化,然後再調用正確的HTTP處理常式並呈現被請求的資源,最後將所產生的標記返回給 IIS 和請求用戶端(如所示)。
IIS 和 ASP.NET 正在處理請求
如果你想瞭解更多關於HttpModules的資料,請自行查閱。
2. 方案設計
2.1. 開發環境
程式設計語言:C#2.0
開發工具:Visual Studio.NET2008
作業系統:windows2003 R2
2.2. 概要設計
使用HttpModules實現IP過濾功能的核心思想是:自訂一個HttpModule捕獲每一個使用者請求,然後擷取相關的使用者IP和被請求的Url進行邏輯判斷,將未授權的請求重新導向到一個錯誤提示頁。Http請求授權與否的判斷邏輯為:
1) 判斷請求是否來自本機電腦,是則自動忽略,否則繼續;
2) 判斷使用者IP是否屬於內網(IP列表),是則忽略,否則繼續;
3) 判斷被請求Url是否授權所有使用者訪問,是則忽略,否則繼續;
4) 將請求重新導向到錯誤提示頁。
在HttpApplication的BeginRequest事件中附加自訂的處理常式即可完成Http請求的捕獲。此外,為了便於維護我們應將程式運行需要的各項參數(IP列表、Url列表、錯誤提示頁路徑等等)儲存於特定的XML設定檔中,為了提高效率,我們還需要將設定檔執行記憶體級的緩衝處理並對IP、Url匹配演算法進行適當的最佳化。
2.3. 配置緩衝演算法
設定檔的緩衝參照微軟CommonServer項目中的實現邏輯,將配置資訊持久化為實體類儲存於HttpContext.Current.Cache中,設定檔發生後緩衝資訊將自動清空,下次訪問時再次執行持久化操作,不需重啟網站。本文對CommonServer的緩衝邏輯不做深入探討,感興趣者可自行搜尋相關資料。
2.4. IP列表演算法
通過上文可知,當前項目用到的IP列表包含的資料量非常有限,就是電廠web伺服器可有效識別的內網IP的窮舉。
因而我們將整個IP列表緩衝,使用時直接檢索目前使用者IP是否存在於列表之中即可。在具體IP的儲存方面,我們可將其視作256進位,將IP字串轉換為數字格式(例如:192.168.10.3可視作192*256*256*256+168*256*256+10*256+3=3232238083,不考慮IPV6);在參數配置的格式方面,我們應同時支援單個IP或IP段的方式增刪IP列表。
2.5. Url列表演算法
就具體需求而言,Url列表是一個授權外網使用者訪問的白名單,換個說法,“對外網使用者而言除了在列表之中的其他都不可訪問”,一旦資料的安全層級降低,會不會出現“對外網使用者而言除了列表之中的其他都可以訪問”的情況出現呢?為了相容這種後續情境,我們需要為Url列表定義一個“是否黑名單”(IsBlacklist)的附加參數;另外,對於動態網站窮舉Url顯然是不現實的,不管是維護黑名單還是白名單,所以我們可以轉變一下思路,更改最終Url為Regex,即:維護一個可匹配目標Url的Regex列表,針對使用者請求的具體Url逐個Regex執行匹配操作,只要有一個匹配成功則認為當前Url存在於Url列表之中。
3. 編碼實現
由於本文提供全部的c#源碼下載,所以本節僅對源碼壓縮包中的主要檔案進行簡要說明:
DotCommon.WebsiteFilter
│ DotCommonWebsiteFilter.cfg.xml
│ WebsiteFilterConfiguration.cs
│ WebsiteFilterHttpModule.cs
├─Util
│ GlobesCache.cs
│ XmlAttributeReader.cs
└─WebsiteFilter
IPMatchEngine.cs
UrlMatchCondition.cs
UrlMatchEngine.cs
- DotCommonWebsiteFilter.cfg.xml
運行參數設定檔
- WebsiteFilterConfiguration.cs
設定檔實體類
- WebsiteFilterHttpModule.cs
實現了System.Web.IHttpModule介面的自訂Http模組
- GlobesCache.cs
全域緩衝操控類
- XmlAttributeReader.cs
xml節點屬性讀取器
- IPMatchEngine.cs
IP匹配引擎
- UrlMatchCondition.cs
Url匹配條件(與Regex匹配)
- UrlMatchEngine.cs
Url匹配引擎
WebsiteFilterHttpModule.cs中BeginRequest自訂處理常式的核心代碼如下:
void context_BeginRequest(object sender, EventArgs e)
{
if (HttpContext.Current.Request.IsLocal)//忽略本機電腦請求
return;
string ip = HttpContext.Current.Request.UserHostAddress;
if (!WebsiteFilterConfiguration.GetConfig().PickedIPs.IsMatch(ip))
{ //若在IP列表中找不到訪客ip
string rawUrl = HttpContext.Current.Request.RawUrl;
UrlMatchEngine pu = WebsiteFilterConfiguration.GetConfig().PickedUrls;
//列表包含當前url且列表為黑名單、列表不包含當前url且列表不為黑名單 時需轉向
//換而言之,“配備結果”與“是否黑名單”取值一致時需轉向
if (pu.IsMatch(rawUrl) == pu.IsBlacklist)
{ //非公開url自動重新導向
HttpContext.Current.Response.Redirect(pu.ErrorPage);
}
}
}
4. 部署應用
4.1. DotCommonWebsiteFilter.cfg.xml設定檔
設定檔的根節點為DotCommon,所有配置資訊均為WebsiteFilter節點的子項。PickedUrl節點對應Url列表,IsBlacklist(1是0否)指示是否為黑名單,ErrorPage指定錯誤提示頁路徑,其子節點add可重複出現,通過pattern屬性指定Regex文本,所示配置表示僅網站首頁(default.aspx)允許外網使用者訪問。
PickedIP節點對應IP列表,有效子節點包括add、remove、clear三項。以為例,第一個add指示內網ip為192.168.10.1、192.168.10.2、192.168.10.3、192.168.10.4、192.168.10.5五個;到第二行刪除掉192.168.10.2、192.168.10.3、192.168.10.4還剩192.168.10.1、192.168.10.5兩個;到第三行再添加上192.168.10.3,最終的內網IP列表為192.168.10.1、192.168.10.3、192.168.10.5三個。
4.2. 在企業網站中整合
- 配置好DotCommonWebsiteFilter.cfg.xml中的各項參數並拷貝到網站根目錄。
- 拷貝DotCommon.WebsiteFilter.dll檔案到網站bin目錄。
- 在網站根目錄下建立與設定檔中相對應的錯誤提示頁(例如sorry.htm)。
- 修改Web.config在<httpModules>節點下註冊WebsiteFilter模組,代碼如下:<httpModules>
<add name="WebsiteFilter"
type="DotCommon.WebsiteFilterHttpModule, DotCommon.WebsiteFilter"/>
</httpModules>
- 分別從內網、外網訪問企業網站查看運行效果。
結束語
本文僅針對具體需求闡述解決方案的構思過程,希望對讀者能有所協助,歡迎提出改進意見。
源碼(2009-6-29):http://files.cnblogs.com/cncxz/DotCommon_WebsiteFilter.rar