经验贴-基于Vc++开发IIS7以及IIS6的万能筛选器

前段时间,公司打算自行开发能够跨j2ee以及.net的单点登录平台,需要一个无侵入式的工具,来拦截所有经过iis的http请求(.net框架的拦截器不可行,因为它无法拦截asp的请求)。最终找到了可以完美实现需求的方案,就是通过iis+isqpi组件的方式,只是这方面的技术资料较少,所以很是费了一番周折才最终完成,在这里给大家做个分享:

一、iis6篇

1)环境:visual studio6,是的,就是这个古老的开发工具,只有这个工具才自带了isapi的模板,因为iis6也足够古老了,它们都是“微软6系”。

2)新建工程:准备好vs6以后,新建新工程,选择"ISAPI Filter Wizard" 工程类型,一路下一步之后,工程目录就出来了(暂时将我们初始化的filter类命令为CTestFilter)。

3)打开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的内容。

4)还是用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头都被去掉了。

5)部署:编译完成刚才的工程后,我们可以得到一个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!!!”。

时间: 2024-09-10 21:18:23

经验贴-基于Vc++开发IIS7以及IIS6的万能筛选器的相关文章

在VC++开发环境中整合Pro*C/C++

c++ 本文所讨论的内容基于以下环境:Microsoft Visual C++ 6.0ORACLE 8i (8.1.7) 当前版本:1.0 (041221) 声明:本文所述的某些操作可能对系统产生重大影响,请慎重操作!本人不对此产生的任何后果负责! 在VC++开发环境中整合Pro*C/C++Pro*C/C++为C/C++语言访问ORACLE数据库提供了极大的方便,但是,在编译的时候往往需要在命名行模式下编译pc文件,而目前多数开发都是在VC++这种整合开发环境中完成的,要在两者之间不停的切换,不

vc++开发的activex控件能否移植到移动平台?

问题描述 vc++开发的activex控件能否移植到移动平台? 我的这个组件,最底层是用的c,基于sdk+wtl. 我想问的是这个能否移植到android等移动平台??如果可以, 大体该从哪些方面着手?? 解决方案 可以明确地说,不可以. 解决方案二: 当然了,如果你按照相同的逻辑,在android上用java重写一套,那可以. 解决方案三: visual c++的编译器都不能产生arm机器码 解决方案四: 目前没很好的办法 还是d?对应实现吧 解决方案五: ActiveX是windows特有的

网易公司基于 OpenStack 开发的一套云计算管理平台

[编者按]OpenStack自 2010 年项目成立以来,已经有超过 200 个公司加入了 OpenStack 项目,目前参与 OpenStack 项目的开发人员有 17,000+,而且这些数字还在增加,作为一个开源的IaaS实现,目前在企业的应用越来越普遍,网易公司私有云团队分享了他们在基于OpenStack 开发的一套云计算管理平台的实战经验,期待和广大的OpenStack 使用者进行交流. 以下为原文: 本文为您介绍了网易公司基于 OpenStack 开发的一套云计算管理平台,以及在开发.

基于.net开发chrome核心浏览器【二】

原文:基于.net开发chrome核心浏览器[二] 一: 上一篇的链接: 基于.net开发chrome核心浏览器[一] 二: 相关资源介绍: chrome Frame: 让IE有一颗chrome的心,看起来不错,但我没有深入研究这个东西. http://www.google.com/chromeframe?hl=zh-CN&quickenable=true https://developers.google.com/chrome/chrome-frame/ WebKit.Net 已经有一段时间没

如何基于Yarn开发你的分布式程序

前一段时间自己开发了一套基于Yarn的容器调度系统,这篇文章就是分享其中的一些经验. 前言 这篇文章不会具体教你如何使用Yarn的API,但是会教你我实践过后的一些经验.接下来的内容会探讨以下两个主题: 基于Yarn开发分布式程序需要做的一些准备工作 基于Yarn开发容器调度系统的一些基本思路 基于Yarn开发分布式程序需要做的一些准备工作 肯定不能撸起袖子就开始干.你思考代码组织,那么你会是一个好的工程师.如果你开始思考系统分层结构,你会是一个好的架构师.当然,最好是都要思考一下啦. Yarn

基于 Quartz 开发企业级任务调度应用

原文地址:https://www.ibm.com/developerworks/cn/opensource/os-cn-quartz/ Quartz 是 OpenSymphony 开源组织在任务调度领域的一个开源项目,完全基于 Java 实现.作为一个优秀的开源调度框架,Quartz 具有功能强大,应用灵活,易于集成的特点.本文剖析了 Quartz 框架内部的基本实现原理,通过一些具体实例描述了应用 Quartz 开发应用程序的基本方法,并对企业应用中常见的问题及解决方案进行了讨论. Quart

【Eclipse插件开发】基于WTP开发自定义的JSP编辑器(十)

WTP TLD内容模型介绍 前面的系列文章中,我们已经分析了WTP的语法Document(IStructuredDocument)和语义Documnt (ICSSDocument或者IDOMDocument)以及和二者密切相关的IStructuredModel,并在这基础之上对WTP默 认提供的StructuredTextEditor进行了部分功能定制. 开发]基于WTP开发自定义的JSP编辑器(十)-eclipse jsp编辑器"> 问题出现了,我们想要的信息全部包含在IStructur

【Eclipse插件开发】基于WTP开发自定义的JSP编辑器(九)

定制StructuredTextEditor源码即时校验 上一节我们定制了WTP StructuredTextEditor的自动提示功能特征,本节将定制另外一个功能特征即 时源码校验.所谓源码即时校验,就是在用户编辑过程中(并未保存),针对用户编辑的内容改变做即时 校验,并给用户即时反馈相关的错误或者其他类型的提示信息.在本节中,我们将以标签的即时校验为例 ,演示如何定制WTP StructuredTextEditor的源码即时校验. 在定制之前,我们先来看一下WTP StructuredTex

【Eclipse插件开发】基于WTP开发自定义的JSP编辑器(八)

定制StructuredTextEditor自动提示 前面介绍的内容集中在两点:StructuredTextEditor框架和WTP数据模型,在本节中就可以定制一个我 们最常用的WTP StructuredTextEditor的功能,那就是自动提示. [WTP StructuredTextEditor提示功能实现分析] 有关Eclipse文本编辑器框架.JFace Text Framework和WTP StructuredTextEditor的简要知识,参见: [Eclipse插件开发]基于WT