




已阅读5页,还剩15页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
使用gSOAP开发实例 自定义header实现用户名令牌认证(Usernametoken Authentication)上一节介绍了怎样实现基本认证(Basic Authentication,以下简称basic方式),望文生义,也就是最简单的用户验证方式,本节稍微深入一些,介绍用户名令牌认证(Usernametoken Authentication,以下简称usernametoken方式)。 Usernametoken方式与basic方式不同的地方,在于后者会把用户名和密码以摘要(digest)的形式,置于HTTP信息头,而前者则把用户名以明文的形式、密码以明文或者摘要的形式,嵌入到一段XML文本中,再置于SOAP消息头当中。 如果使用soapUI调试客户端程序的话,会发现以下是basic方式发出的完整的SOAP消息: POST /Services/ECHO HTTP/0.9 Content-Type: text/xml;charset=UTF-8 SOAPAction: User-Agent: Jakarta Commons-HttpClient/3.1 Content-Length: 292 Authorization: Basic VkYtSEstbVNNST0OdlR42EMZaD1BMyE= Host: Cookie: $Version=0; MSP2LB=test2.test2f02; $Path=/ hello 以下是usernametoken方式发出的完整的SOAP消息: POST /4.0/services/SecureEcho HTTP/1.1 Content-Type: text/xml;charset=UTF-8 SOAPAction: User-Agent: Jakarta Commons-HttpClient/3.1 Host: Content-Length: xxx roy liang LX4gh+njbEtCNAtkWkXDYA= 2010-08-11T06:02:25.874Z G06164 hello 其中,加粗部分表示两者的主要区别,红字部分表示各自必不可少的元素。 由此可以看出,usernametoken方式的特点,是在SOAP的header中加入一个Security标签,把usernametoken信息放在这个Security标签里。至于header里的另外一个customerId标签,是该应用自身的额外要求。 gSOAP实现basic方式相对简单,不过实现usernametoken就比较复杂。gSOAP的用户指南推荐使用其自带的插件,在samples/wsse目录下的官方实例也是使用这个插件。但是,我个人认为这种方法比较累赘,自动生成的代码太多,不易看懂,而且似乎非常依赖于wsdl本身的写法。比如,samples/wsse目录下的官方实例含有下列表示SOAP header的结构体,但是,在我实际开发的应用并没有自动产生,即使强行加上去,编译执行通过,运行的时候也出现了相当多的错误。structSOAP_ENV_Headerstruct_wsse_Security*wsse_Security; 而且,从理论上讲,gSOAP不过是一个框架,定义了从SOAP对象到SOAP消息,以及从SOAP消息到SOAP对象的序列化过程,并且提供了一套与之相适应的API,使用gSOAP开发不过是在其框架范围内调用其API编程。框架的弊端,可想而知,限制了灵活,也限制了方便,更限制了创新。所以,我们可以使用gSOAP编程,但是也许没有必要全部照搬,至少在这个案例中,就没有必要照搬。 我们应有的思路是,既然customerId可以直接写到SOAP header中,那么与之并列的、含有usernametoken的Security也可以直接写到SOAP header中,完全不需要依赖于gSOAP的wsse插件。 与上节一样,基于保密原则,本节案例采用的wsdl同样是经过裁剪和替换的,内容如下: 在gSOAP的wsdl目录,按以下步骤建立客户端存根程序: -bash-3.2$ mkdir p secure_echo -bash-3.2$ cd secure_echo -bash-3.2$ ./wsdl2h c o secure_echo.h secure_echo.wsdl -bash-3.2$ ././src/soapcpp2 C L x secure_echo.h 重点来了,此时需要修改gSOAP为你自动生成的部分文件,加入usernametoken支持,以下是详细步骤,代码中加粗部分即修改的内容: 1. soapStub.h,搜索SOAP_ENV_Header结构体,本案例中,gSOAP只为我们自动生成了对应customerId的指针,因为需要在SOAP header中增加用户名和密码,所以要在这里手动添加这些信息。这样,修改后的SOAP_ENV_Header变为: struct SOAP_ENV_Header char *wsse_username; /* mustUnderstand */ char *wsse_password; /* mustUnderstand */ char *ns1_customerId;/* mustUnderstand */ ; 2. soapC.c,搜索soap_out_SOAP_ENV_Header函数,这是客户端把SOAP对象转化为SOAP消息相关的函数,由于gSOAP只是自动生成了customerId属性的转化,我们还需要加入Security属性,按照soapUI测试好的结果,Security含有一个UsernameToken,而UsernameToken又含有用户名和密码,因此soap_out函数应当这样写: SOAP_FMAC3 int SOAP_FMAC4 soap_out_SOAP_ENV_Header(struct soap *soap, const char *tag, int id, const struct SOAP_ENV_Header *a, const char *type) if (soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a, SOAP_TYPE_SOAP_ENV_Header), type) return soap-error; if (soap_element_begin_out(soap, wsse:Security, -1, ) | soap_element_begin_out(soap, wsse:UsernameToken, -1, ) | soap_out_string(soap, wsse:Username, -1, &a-wsse_username, ) | soap_out_string(soap, wsse:Password, -1, &a-wsse_password, /wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText) | soap_element_end_out(soap, wsse:UsernameToken) | soap_element_end_out(soap, wsse:Security) ) return soap-error; soap-mustUnderstand = 1; if (soap_out_string(soap, ns1:customerId, -1, &a-ns1_customerId, ) return soap-error; return soap_element_end_out(soap, tag); 3. SecureEchoHttpBinding.nsmap,由于上一步用到了wsse这个namespace,而它又没有出现在nsmap文件中,因此我们需要增加该命名空间的信息,其URL同样可以从soapUI测试结果中取得: SOAP_NMAC struct Namespace namespaces = SOAP-ENV, /soap/envelope/, /*/soap-envelope, NULL, SOAP-ENC, /soap/encoding/, /*/soap-encoding, NULL, xsi, /2001/XMLSchema-instance, /*/XMLSchema-instance, NULL, xsd, /2001/XMLSchema, /*/XMLSchema, NULL, ns1, , NULL, NULL, wsse, /wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd, NULL, NULL, NULL, NULL, NULL, NULL ; 存根程序修改完成,就可以开始编写客户端程序代码了。这个web service提供了两个接口,一个是secure_echo,也就是客户端送任意信息上来,服务端就返回相同的字符串,代码如下: #includesoapH.h#includeSecureEchoHttpBinding.nsmapintmain(intargc,char*argv)if(argc!=5&argc!=6)printf(Usage:%susernamepasswordcustomer_idmessageend_pointn,argv0);exit(-1);structsoapsoap;soap_init(&soap);struct_ns1_sendEchorequest;struct_ns1_sendEchoResponseresponse;soap_ssl_init();if(soap_ssl_client_context(&soap,SOAP_SSL_NO_AUTHENTICATION,NULL,NULL,NULL,NULL,NULL)soap_print_fault(&soap,stderr);exit(-1);structSOAP_ENV_Headerheader;header.wsse_username=argv1;header.wsse_password=argv2;header.ns1_customerId=argv3;soap.header=&header;/soap_write_SOAP_ENV_Header(&soap,&header);request.message=argv4;char*endpoint=NULL;if(argc=6)endpoint=argv5;printf(username:%sn,header.wsse_username);printf(password:%sn,header.wsse_password);printf(customerid:%sn,header.ns1_customerId);printf(message:%sn,request.message);if(endpoint)printf(endpoint:%sn,endpoint);if(soap_call_ns1_sendEcho(&soap,endpoint,NULL,&request,&response)=SOAP_OK)printf(%sn,response.out);elsesoap_print_fault(&soap,stderr);soap_destroy(&soap);soap_end(&soap);soap_done(&soap);return0; 另外一个是显示版本信息,代码如下: #includesoapH.h#includeSecureEchoHttpBinding.nsmapintmain(intargc,char*argv)if(argc!=4&argc!=5)printf(Usage:%susernamepasswordcustomer_idend_pointn,argv0);exit(-1);structsoapsoap;soap_init(&soap);struct_ns1_showVersionInformationrequest;struct_ns1_showVersionInformationResponseresponse;soap_ssl_init();if(soap_ssl_client_context(&soap,SOAP_SSL_NO_AUTHENTICATION,NULL,NULL,NULL,NULL,NULL)soap_print_fault(&soap,stderr);exit(-1);structSOAP_ENV_Headerheader;header.wsse_username=argv1;header.wsse_password=argv2;header.ns1_customerId=argv3;soap.header=&header;/soap_write_SOAP_ENV_Header(&soap,&header);char*endpoint=NULL;if(argc=5)endpoint=argv4;printf(username:%sn,header.wsse_username);printf(password:%sn,header.wsse_password);printf(customerid:%sn,header.ns1_customerId);if(endpoint)printf(endpoint:%sn,endpoint);if(soap_call_ns1_showVersionInformation(&soap,endpoint,NULL,&request,&response)=SOAP_OK)printf(%sn,response.out);elsesoap_print_fault(&soap,stderr);soap_destroy(&soap);soap_end(&soap);soap_done(&soap);return0; 两个客户端程序都是一样的结构,仅仅是调用的接口不一样。与上一节的basic方式的客户端相比,usernametoken方式的用户密码不是保存在soap结构体的userid变量和passwd变量中,而是保存在header指针指向的SOAP_ENV_Header结构体中,这就是刚才我们为什么要修改SOAP_ENV_Header结构体的原因。 两个客户端程序分别保存为secure_echo.c和show_version.c,编译命令分别是: gcc -DWITH_OPENSSL -O2 -o secure_echo secure_echo.c soapC.c soapClient.c ././stdsoap2.c -I./. -L./. -lgsoap -lssl gcc -DWITH_OPENSSL -O2 -o show_version show_version.c soapC.c soapClient.c ././stdsoap2.c -I./. -L./. -lgsoap lssl 由此可见,编译的源代码和链接的库文件都与上一节的没什么区别,没有使用额外的插件。 客户端程序搞掂,然后就是服务端了。 回到gSOAP的wsdl目录,按以下步骤建立服务端存根程序: -bash-3.2$ mkdir p secure_echo_server -bash-3.2$ cd secure_echo_server -bash-3.2$ cp p ./secure_echo/secure_echo.h . -bash-3.2$ ././src/soapcpp2 S L x secure_echo.h 与客户端程序一样,服务端同样要进行一些修改,步骤如下: 1. soapStub.h,与客户端的修改是一样的 2. soapC.c,搜索soap_in_SOAP_ENV_Header函数,注意客户端修改的是soap_out函数,这里是soap_in函数,是服务端把SOAP消息转化为SOAP对象的函数。由于客户端送上来的SOAP header消息含有Security属性,我们需要把它转为username和password。修改后的代码如下,加粗部分是修改内容: SOAP_FMAC3 struct SOAP_ENV_Header * SOAP_FMAC4 soap_in_SOAP_ENV_Header(struct soap *soap, const char *tag, struct SOAP_ENV_Header *a, const char *type) size_t soap_flag_wsse_security = 1; size_t soap_flag_ns1_customerId = 1; if (soap_element_begin_in(soap, tag, 0, type) return NULL; a = (struct SOAP_ENV_Header *)soap_id_enter(soap, soap-id, a, SOAP_TYPE_SOAP_ENV_Header, sizeof(struct SOAP_ENV_Header), 0, NULL, NULL, NULL); if (!a) return NULL; soap_default_SOAP_ENV_Header(soap, a); if (soap-body & !*soap-href) for (;) if ( soap_flag_wsse_security ) if ( soap_element_begin_in(soap, NULL, 0, NULL) ) return NULL; if ( soap_element_begin_in(soap, NULL, 0, NULL) ) return NULL; if ( soap_in_string(soap, , &a-wsse_username, ) & soap_in_string(soap, , &a-wsse_password, ) ) soap_flag_wsse_security-; soap_element_end_in(soap, NULL); soap_element_end_in(soap, NULL); soap-error = SOAP_TAG_MISMATCH; if (soap_flag_ns1_customerId & (soap-error = SOAP_TAG_MISMATCH | soap-error = SOAP_NO_TAG) if (soap_in_string(soap, ns1:customerId, &a-ns1_customerId, xsd:string) soap_flag_ns1_customerId-; continue; if (soap-error = SOAP_TAG_MISMATCH) soap-error = soap_ignore_element(soap); if (soap-error = SOAP_NO_TAG) break; if (soap-error) return NULL; if (soap_element_end_in(soap, tag) return NULL; else a = (struct SOAP_ENV_Header *)soap_id_forward(soap, soap-href, (void*)a, 0, SOAP_TYPE_SOAP_ENV_Header, 0, sizeof(struct SOAP_ENV_Header), 0, NULL); if (soap-body & soap_element_end_in(soap, tag) return NULL; return a; 服务端程序如下,到这里就没什么难度了,基本上与上一节的服务端结构差不多,注意要把几个pem证书拷贝过来(因为usernametoken方式通常都是基于HTTPS的),echo接口和show_version接口都要编写: #include#includesoapH.h#includeSecureEchoHttpBinding.nsmapvoid*process_request(void*soap)pthread_detach(pthread_self();if(soap_ssl_acce
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 橡胶育苗工中秋节后复工安全考核试卷含答案
- 外架工安全作业规程培训课件
- 金属材酸洗工中秋节后复工安全考核试卷含答案
- 临床检验类设备组装调试工国庆节后复工安全考核试卷含答案
- 行使涤除权合同(标准版)
- 印泥制作工国庆节后复工安全考核试卷含答案
- 电信行业客户服务优化策略
- 美容医疗机构规范检查操作手册
- SNCR与SCR脱硝技术应用方案
- 外来施人员安全培训课件
- 外科学-第十一章-外科感染(含案例分析)课件
- 《ch棘皮动物》课件
- 急诊科岗位职责
- 中国服用过兴奋剂运动员名单 兴奋剂真的是毒品吗
- 小学英语语法时态讲解与归纳
- 《生存与修炼》熊厚音讲《道德经》教学文案
- 淘宝新店运营计划书文献
- 产教融合校企合作[可修改版ppt]课件
- ICH Q6B 生物技术产品和生物制品的检验方法和可接受标准
- 12贮水花盆案例总结-2015天津中心修改43
- (精心整理)六方最密堆积空间利用率和密度的计算
评论
0/150
提交评论