使用gSOAP开发实例(7) 基于HTTPS的基本认证(Basic Authentication)

http://blog.csdn.net/yui/article/details/5817771

经过前几节的讲解,相信大家都能够熟练地开发 gSOAP 应用程序,甚至跨平台也不是问题。但是,诸如 stock、 weather 、 exchange 这些应用都是面向大众提供的免费资源,不是企业级的应用,绝大多数都不需要用户认证。而那些商业化的应用却恰恰相反,大部分都要求客户端提供这样那样的验证。

 

由于有认证的免费资源实在难找,我只好把公司正在使用的一个服务的 wsdl 裁剪一下,拿到这里作为实例,裁剪后的 wsdl 只保留一个 echo 接口,顾名思义,就是客户端送什么字符串上来,服务端就返回同样的字符串。这个 wsdl 如下(业务相关的网址和 end point 均已作了特别处理):

 

 

[xhtml]  view plain copy
  1. <?xml version='1.0' encoding='UTF-8'?>  
  2. <s0:definitions name="ServicesDefinitions" targetNamespace="http://echo.rsecure.com/ECHO" xmlns="" xmlns:s0="http://schemas.xmlsoap.org/wsdl/" xmlns:s1="http://echo.rsecure.com/ECHO" xmlns:s2="http://schemas.xmlsoap.org/wsdl/soap/">  
  3.   <s0:types>  
  4.     <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://echo.rsecure.com/ECHO" xmlns:s0="http://schemas.xmlsoap.org/wsdl/" xmlns:s1="http://echo.rsecure.com/ECHO" xmlns:s2="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xs="http://www.w3.org/2001/XMLSchema">  
  5.       <xs:element name="echo">  
  6.         <xs:complexType>  
  7.           <xs:sequence>  
  8.             <xs:element name="EchoMessage" type="xs:string"/>  
  9.           </xs:sequence>  
  10.         </xs:complexType>  
  11.       </xs:element>  
  12.       <xs:element name="echoResponse">  
  13.         <xs:complexType>  
  14.           <xs:sequence>  
  15.             <xs:element name="Echo" type="xs:string"/>  
  16.           </xs:sequence>  
  17.         </xs:complexType>  
  18.       </xs:element>  
  19.     </xs:schema>  
  20.   </s0:types>  
  21.   <s0:message name="echo">  
  22.     <s0:part element="s1:echo" name="parameters"/>  
  23.   </s0:message>  
  24.   <s0:message name="echoResponse">  
  25.     <s0:part element="s1:echoResponse" name="Echo"/>  
  26.   </s0:message>  
  27.   <s0:portType name="LMIAPort">  
  28.     <s0:operation name="echo" parameterOrder="parameters">  
  29.       <s0:input message="s1:echo"/>  
  30.       <s0:output message="s1:echoResponse"/>  
  31.     </s0:operation>  
  32.   </s0:portType>  
  33.   <s0:binding name="ServicesSoapBinding" type="s1:LMIAPort">  
  34.     <s2:binding style="document" mce_style="document" transport="http://schemas.xmlsoap.org/soap/http"/>  
  35.     <s0:operation name="echo">  
  36.       <s2:operation style="document" mce_style="document"/>  
  37.       <s0:input>  
  38.         <s2:body parts="parameters" use="literal"/>  
  39.       </s0:input>  
  40.       <s0:output>  
  41.         <s2:body parts="Echo" use="literal"/>  
  42.       </s0:output>  
  43.     </s0:operation>  
  44.   </s0:binding>  
  45.   <s0:service name="Services">  
  46.     <s0:port binding="s1:ServicesSoapBinding" name="lmiAPort">  
  47.       <s2:address location="https://localhost:6883"/>  
  48.     </s0:port>  
  49.   </s0:service>  
  50. </s0:definitions>  

 

在 gsoap-2.7/gsoap/wsdl/ 目录下建立两个目录: echo 和 echo_server ,按照前几节的方法分别建立gSOAP 客户端和服务端。客户端与前几节的相比,首先是增加了 soap­_ssl_client_context 处理 HTTPS 协议。其次,本案例使用的是基本认证 (Basic Authentication) ,需要在 soap 变量初始化之后给出用户名和密码。

        struct soap soap;

        soap_init(&soap);

        soap.userid = argv[1];

        soap.passwd = argv[2];

 

客户端完整程序如下:

 

 

[cpp]  view plain copy
  1. #include "soapH.h"  
  2. #include "ServicesSoapBinding.nsmap"  
  3. int main(int argc, char **argv) {  
  4.     if ( argc != 4 && argc != 5 ) {  
  5.         printf("Usage: %s username password message [end_point]/n", argv[0]);  
  6.         exit(-1);  
  7.     }  
  8.     struct soap soap;  
  9.     soap_init(&soap);  
  10.     soap.userid = argv[1];  
  11.     soap.passwd = argv[2];  
  12.     struct _ns1__echo request;  
  13.     struct _ns1__echoResponse response;  
  14.     soap_ssl_init();  
  15.     if ( soap_ssl_client_context(&soap, SOAP_SSL_NO_AUTHENTICATION, NULL, NULL, NULL, NULL, NULL) ) {  
  16.         soap_print_fault(&soap, stderr);  
  17.         exit(-1);  
  18.     }  
  19.     request.EchoMessage = argv[3];  
  20.     char *endpoint = NULL;  
  21.     if ( argc == 5 )  
  22.         endpoint = argv[4];  
  23.     printf("username  : %s/n", soap.userid);  
  24.     printf("password  : %s/n", soap.passwd);  
  25.     printf("message   : %s/n", request.EchoMessage);  
  26.     if ( endpoint )  
  27.         printf("end point : %s/n", endpoint);  
  28.     if ( soap_call___ns1__echo(&soap, endpoint, NULL, &request, &response) == SOAP_OK ) {  
  29.         printf("%s/n", response.Echo);  
  30.     }  
  31.     else {  
  32.         soap_print_fault(&soap, stderr);  
  33.     }  
  34.     soap_destroy(&soap);  
  35.     soap_end(&soap);  
  36.     soap_done(&soap);  
  37.     return 0;  
  38. }  

 

保存为 echo.c ,编译命令如下,注意增加了 -DWITH_OPENSSL 参数,以及需要链接 libssl 库。

gcc -DWITH_OPENSSL -O2 -o echo echo.c soapC.c soapClient.c ../../stdsoap2.c -I../.. -L../.. -lgsoap –lssl

 

服务端的编写相对麻烦,以下给出一个最简单的实现。与第二节的 stock 服务端程序相比,主要是增加了soap_ssl_server_context 处理 HTTPS 协议,其中需要用到 gsoap-2.7.17 自带的 ssl 实例程序中的几个pem 证书,把它们拷贝过来即可使用。另外,与不需要认证的应用相比, __ns1__echo 增加了用户密码校验。这个案例里,设定客户端送上来的用户 / 密码应当为 roy/liang ,否则将返回 401 错误。

 

[cpp]  view plain copy
  1. #include <pthread.h>  
  2. #include "soapH.h"  
  3. #include "ServicesSoapBinding.nsmap"  
  4. void *process_request(void *soap) {  
  5.     pthread_detach(pthread_self());  
  6.     if ( soap_ssl_accept((struct soap *) soap) != SOAP_OK )  
  7.         soap_print_fault((struct soap *) soap, stderr);  
  8.     else  
  9.         soap_serve((struct soap *) soap);  
  10.     soap_end((struct soap *) soap);  
  11.     soap_free((struct soap *) soap);  
  12.     return NULL;  
  13. }  
  14. int main(int argc, char **argv) {  
  15.     if ( argc != 2 ) {  
  16.         printf("Usage: %s port/n", argv[0]);  
  17.         exit(-1);  
  18.     }  
  19.     int port = atol(argv[1]);  
  20.     pthread_t tid;  
  21.     struct soap *tsoap;  
  22.     struct soap soap;  
  23.     soap_init(&soap);  
  24.     soap_ssl_init();  
  25.     if ( soap_ssl_server_context(&soap, SOAP_SSL_DEFAULT, "server.pem""password""cacert.pem", NULL, "dh512.pem", NULL, argv[0]) ) {  
  26.         soap_print_fault(&soap, stderr);  
  27.         exit(-1);  
  28.     }  
  29.     int m, s;  
  30.     if ( (m = soap_bind(&soap, NULL, port, 100)) < 0 ) {  
  31.         soap_print_fault(&soap, stderr);  
  32.     }  
  33.     else {  
  34.         printf("Socket connect successfully: master socket = %d/n", m);  
  35.         int i = 0;  
  36.         while ( 1 ) {  
  37.             if ( (s = soap_accept(&soap)) < 0 ) {  
  38.                 soap_print_fault(&soap, stderr);  
  39.                 break;  
  40.             }  
  41.             printf("Connection %d accepted from IP = %d.%d.%d.%d, slave socket = %d/n", ++i, (soap.ip >> 24) & 0xff, (soap.ip >> 16) & 0xff, (soap.ip >> 8) & 0xff, soap.ip & 0xff, s);  
  42.             tsoap = soap_copy(&soap);  
  43.             if ( !tsoap ) {  
  44.                 soap_closesock(&soap);  
  45.                 continue;  
  46.             }  
  47.             pthread_create(&tid, NULL, &process_request, (void *) tsoap);  
  48.         }  
  49.     }  
  50.     soap_done(&soap);  
  51.     return 0;  
  52. }  
  53. int __ns1__echo(  
  54.     struct soap *soap,  
  55.     struct _ns1__echo *request,  
  56.     struct _ns1__echoResponse *response) {  
  57.     if ( !soap->userid || !soap->passwd || strcmp(soap->userid, "roy") || strcmp(soap->passwd, "liang") )  
  58.         return 401;  
  59.     int len = strlen(request->EchoMessage);  
  60.     response->Echo = (char *) malloc(sizeof(char) * (len + 1));  
  61.     strcpy(response->Echo, request->EchoMessage);  
  62.     return SOAP_OK;  
  63. }  


 

保存为 echo_server.c ,编译命令是:

gcc -DWITH_OPENSSL -O2 -o echo_server echo_server.c soapC.c soapServer.c ../../stdsoap2.c -I../.. -L../.. -lgsoap -lssl -lcrypto –lpthread

 

客户端和服务端都编译完成后,首先启动服务端:

-bash-3.2$ ./echo_server 6883

Socket connect successfully: master socket = 3

 

然后,在另一个窗口运行客户端,由于 wsdl 里已经指定默认 end point 是 https://localhost:6883 ,因此,客户端并不需要额外给出。

 

正常的返回结果:

-bash-3.2$ ./echo roy liang hi

username  : roy

password  : liang

message   : hi

hi

 

用户、密码不正确将返回 401 错误:

-bash-3.2$ ./echo roy xxx hi

username  : roy

password  : xxx

message   : hi

Error 401 fault: SOAP-ENV:Server [no subcode]

"HTTP/1.1 401 Unauthorized"

Detail: <?xml version="1.0" encoding="UTF-8"?>

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns1="http://echo.rsecure.com/ECHO"><SOAP-ENV:Body><SOAP-ENV:Fault><faultcode>SOAP-ENV:Client</faultcode><faultstring>HTTP Error: 401 Unauthorized</faultstring></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>

 

基于 HTTP 的基本认证 (Basic Authentication) 比基于 HTTPS 的更加简单,在客户端和服务端的程序去除HTTPS 处理即可,不再赘述。

 

另外,本案例中用到的证书的失效日期好像是 2010 年 11 月 2 日,在此之后执行的结果可能会不一样。

你可能感兴趣的:(使用gSOAP开发实例(7) 基于HTTPS的基本认证(Basic Authentication))