您好,登錄后才能下訂單哦!
基于Vc++開發IIS7以及IIS6的萬能篩選器
一、iis6篇
1)新建工程:準備好vs6以后,新建新工程,選擇"ISAPI Filter Wizard" 工程類型,一路下一步之后,工程目錄就出來了(暫時將我們初始化的filter類命令為CTestFilter)。
2)打開vs初始化的工程,打開CTestFilter.cpp文件,我們可以看到有一個自動生成的函數:
BOOL CTestFilter::GetFilterVersion(PHTTP_FILTER_VERSION pVer)
{
// Call default implementation for initialization
CTestFilter::GetFilterVersion(pVer);
// Clear the flags set by base class
pVer->dwFlags &= ~SF_NOTIFY_ORDER_MASK;
// Set the flags we are interested in
pVer->dwFlags |= SF_NOTIFY_ORDER_HIGH| SF_NOTIFY_PREPROC_HEADERS | SF_NOTIFY_SEND_RAW_DATA;
// Load description string
TCHAR sz[SF_MAX_FILTER_DESC_LEN+1];
ISAPIVERIFY(::LoadString(AfxGetResourceHandle(),
IDS_FILTER, sz, SF_MAX_FILTER_DESC_LEN));
_tcscpy(pVer->lpszFilterDesc, sz);
return TRUE;
}
不要關注其他代碼,只需要關注紅色字體部分,這幾個系統常量是用來指定篩選器需要攔截哪些http內容的,我簡單攔截類型有好幾種,詳細信息可以查詢msdn的文檔,我只簡單介紹下其中的SF_NOTIFY_PREPROC_HEADERS和 SF_NOTIFY_SEND_RAW_DATA,SF_NOTIFY_PREPROC_HEADERS代表在iis處理http的header請求時觸發一次攔截,而SF_NOTIFY_SEND_RAW_DATA代表在iis向客戶端回寫http的response內容前觸發攔截,在這個攔截當中,你可以接收到response的指針,修改response的內容。
3)還是用wizard,去實現CHttpFilter基類的各個攔截方法(需要與前面的GetFilterVersion方法中指定的攔截事件對應,否則iis不會觸發這些方法的),這里我還是介紹下與上一步向對應的OnSendRawData(CHttpFilterContext* pfc, PHTTP_FILTER_RAW_DATA pRawData)和OnPreprocHeaders(CHttpFilterContext* pfc,PHTTP_FILTER_PREPROC_HEADERS pHeaders)兩個函數,大家可以很容易地看出這兩個函數與前面兩個攔截類型的對應關系。OnPreprocHeaders是專門用來在iis處理http請求前,對客戶端的http header進行處理,其中入參pHeaders是個結構型數據,其中包含了一個指向header對象的指針,我們可以拿到這個指針進行預處理,并可以修改headers的內容。
OnSendRawData可以說是這些方法中最強悍的了,它可以拿到iis的response內容,并可以修改response的內容,其中pRawData這個結構型的入參就包含了response內容的指針,還是上代碼給大家講講這個函數是如何去修改一切的吧:
DWORD CTestFilter::OnSendRawData(CHttpFilterContext* pfc, PHTTP_FILTER_RAW_DATA pRawData)
{
// TODO: Add your specialized code here and/or call the base class
CString resp="Hello isapi!!";
void* mem=(void*)pfc->AllocMem(resp.GetLength());//必須使用isapi提供的申請內存方法,才能正常返回http內容,用isapi的方法申請的內存,iis會自動幫你回收
memset(mem,0,resp.GetLength());
memcpy(mem,resp.GetBuffer(0),resp.GetLength());
pRawData->cbInBuffer=resp.GetLength();
pRawData->cbInData=resp.GetLength();
pRawData->pvInData=mem;
return SF_STATUS_REQ_NEXT_NOTIFICATION;
}
這樣修改過后,所有客戶端的請求都變成了”Hello isqpi!!“了,而且連正常的http頭都被去掉了。
4)部署:編譯完成剛才的工程后,我們可以得到一個TestFilter.dll的文件,打開iis6的管理頁面,進入web站點的屬性對話框,到isapi的選項卡中,添加我們剛才生成的dll文件,并重啟iis服務器,ok,iis就只會返回”Hello isapi!!“了。
二、iis7篇
伴隨著windows server2008的出現,iis7也一起推出,iis7雖然也可以通過很曲折的方式,實現isapi接口功能,但根據實踐的情況來開,iis7在使用isapi擴展來實現攔截功能時,經常會出現攔截不了的煩人問題,這時我們在查閱了大量資料后發現,iis7對isapi的支持本來就不好,因為微軟又在iis7這個平臺上推出了一個更強大的攔截實現,那就是iis擴展模塊,iis7的所有功能都是通過加載不同的模塊來實現的,而且為我們實現攔截器特別提供了一個httpmodule的模塊,這個模塊是凌駕于所有http請求的,不管你使用的asp還是asp.net甚至是借用iis做服務器的php服務器,所有的請求都會先經過這個模塊來處理,難怪微軟不會去關注iis7對isapi的兼容問題,它要推新東西了嘛~
下面我們就介紹下如何通過iis的模塊來實現攔截并修改http請求的功能:
1)環境準備:visual studio2008/2005+iis7+windows server2008。
2)新建c++工程:在vs中新建一個空的動態鏈接庫的工程。
3)新建一個CHttpModule的子類CMyHttpModule(需要基于對頭文件httpserv.h的引用),并實現一個虛方法OnBeginRequest:
#define _WINSOCKAPI_
#include <windows.h>
#include <sal.h>
#include <httpserv.h>
class CMyHttpModule: public CHttpModule
{
public:
REQUEST_NOTIFICATION_STATUS
OnBeginRequest(
IN IHttpContext * pHttpContext,
IN IHttpEventProvider * pProvider
)
{
UNREFERENCED_PARAMETER( pProvider );
// 創建一個 HRESULT 來接收方法返回值.
HRESULT hr;
// 獲取一個指向response對象的指針.
IHttpResponse * pHttpResponse = pHttpContext->GetResponse();
if (pHttpResponse != NULL)
{
// 直接清理掉原來的response內容.
pHttpResponse->Clear();
// 設置response的格式.
pHttpResponse->SetHeader(
HttpHeaderContentType,"text/plain",
(USHORT)strlen("text/plain"),TRUE);
PCSTR pszBuffer = "Hello HttpModule!!!";
// 創建一個數據塊.
HTTP_DATA_CHUNK dataChunk;
// 把數據塊類型設置成http類型的(后續的內存清理工作就會由iis容器自己完成).
dataChunk.DataChunkType = HttpDataChunkFromMemory;
DWORD cbSent;
// 給數據塊賦值.
dataChunk.FromMemory.pBuffer =
(PVOID) pszBuffer;
dataChunk.FromMemory.BufferLength =
(USHORT) strlen(pszBuffer);
// 將數據塊插入到response內容中.
hr = pHttpResponse->WriteEntityChunks(
&dataChunk,1,FALSE,TRUE,&cbSent);
if (FAILED(hr))
{
pHttpResponse->SetStatus(500,"Server Error",0,hr);
}
return RQ_NOTIFICATION_FINISH_REQUEST;
}
return RQ_NOTIFICATION_CONTINUE;
}
}
4)新建一個實現了IHttpModuleFactory接口的工廠類,用來注冊攔截模塊和攔截的方式
class CMyHttpModuleFactory : public IHttpModuleFactory
{
public:
HRESULT
GetHttpModule(
OUT CHttpModule ** ppModule,
IN IModuleAllocator * pAllocator
)
{
UNREFERENCED_PARAMETER( pAllocator );
// 實例化一個模塊的指針.
CMyHttpModule * pModule = new CMyHttpModule;
if (!pModule)
{
return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
}
else
{
*ppModule = pModule;
pModule = NULL;
return S_OK;
}
}
void
Terminate()
{
// 清理自己的內存.
delete this;
}
};
// 用來注冊模塊工廠的方法.
HRESULT
__stdcall
RegisterModule(
DWORD dwServerVersion,
IHttpModuleRegistrationInfo * pModuleInfo,
IHttpServer * pGlobalInfo
)
{
UNREFERENCED_PARAMETER( dwServerVersion );
UNREFERENCED_PARAMETER( pGlobalInfo );
// 設置需要攔截的方式,這里設置的是給客戶端返回response內容前,和我們之前iis6的示例類似,可以設置多個,但必須和你的httpmodule中對應.
return pModuleInfo->SetRequestNotifications(
new CMyHttpModuleFactory,
RQ_BEGIN_REQUEST,
0
);
}
5)編譯并生成一個MyHttpModule.dll的動態鏈接庫,將保存MyHttpModule.dll的全路徑添加到%windir%\system32\inetsrv\config\applicationHost.config文件的globalModules節點下,重啟iis,ok,這下訪問你iis中的任何一個文件,返回的內容都會是:“Hello HttpModule!!!”。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。