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

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

 

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

<? xml version='1.0' encoding='UTF-8' ?>
< 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/" >
  
< s0:types >
    
< 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" >
      
< xs:element  name ="echo" >
        
< xs:complexType >
          
< xs:sequence >
            
< xs:element  name ="EchoMessage"  type ="xs:string" />
          
</ xs:sequence >
        
</ xs:complexType >
      
</ xs:element >
      
< xs:element  name ="echoResponse" >
        
< xs:complexType >
          
< xs:sequence >
            
< xs:element  name ="Echo"  type ="xs:string" />
          
</ xs:sequence >
        
</ xs:complexType >
      
</ xs:element >
    
</ xs:schema >
  
</ s0:types >
  
< s0:message  name ="echo" >
    
< s0:part  element ="s1:echo"  name ="parameters" />
  
</ s0:message >
  
< s0:message  name ="echoResponse" >
    
< s0:part  element ="s1:echoResponse"  name ="Echo" />
  
</ s0:message >
  
< s0:portType  name ="LMIAPort" >
    
< s0:operation  name ="echo"  parameterOrder ="parameters" >
      
< s0:input  message ="s1:echo" />
      
< s0:output  message ="s1:echoResponse" />
    
</ s0:operation >
  
</ s0:portType >
  
< s0:binding  name ="ServicesSoapBinding"  type ="s1:LMIAPort" >
    
< s2:binding  style ="document"  transport ="http://schemas.xmlsoap.org/soap/http" />
    
< s0:operation  name ="echo" >
      
< s2:operation  style ="document" />
      
< s0:input >
        
< s2:body  parts ="parameters"  use ="literal" />
      
</ s0:input >
      
< s0:output >
        
< s2:body  parts ="Echo"  use ="literal" />
      
</ s0:output >
    
</ s0:operation >
  
</ s0:binding >
  
< s0:service  name ="Services" >
    
< s0:port  binding ="s1:ServicesSoapBinding"  name ="lmiAPort" >
      
< s2:address  location ="https://localhost:6883" />
    
</ s0:port >
  
</ s0:service >
</ s0:definitions >

 

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

         struct  soap soap;
        soap_init(
& soap);
        soap.userid 
=  argv[ 1 ];
        soap.passwd 
=  argv[ 2 ];
 

客户端完整程序如下:

#include  " soapH.h "
#include 
" ServicesSoapBinding.nsmap "

int  main( int  argc,  char   ** argv) {
        
if  ( argc  !=   4   &&  argc  !=   5  ) {
                printf(
" Usage: %s username password message [end_point]\n " , argv[ 0 ]);
                exit(
- 1 );
        }

        
struct  soap soap;
        soap_init(
& soap);
        soap.userid 
=  argv[ 1 ];
        soap.passwd 
=  argv[ 2 ];

        
struct  _ns1__echo request;
        
struct  _ns1__echoResponse response;

        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 );
        }

        request.EchoMessage 
=  argv[ 3 ];
        
char   * endpoint  =  NULL;
        
if  ( argc  ==   5  )
                endpoint 
=  argv[ 4 ];

        printf(
" username  : %s\n " , soap.userid);
        printf(
" password  : %s\n " , soap.passwd);
        printf(
" message   : %s\n " , request.EchoMessage);
        
if  ( endpoint )
                printf(
" end point : %s\n " , endpoint);

        
if  ( soap_call___ns1__echo( & soap, endpoint, NULL,  & request,  & response)  ==  SOAP_OK ) {
                printf(
" %s\n " , response.Echo);
        }
        
else  {
                soap_print_fault(
& soap, stderr);
        }

        soap_destroy(
& soap);
        soap_end(
& soap);
        soap_done(
& soap);
        
return   0 ;
}

 

保存为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错误。

#include  < pthread.h >

#include 
" soapH.h "
#include 
" ServicesSoapBinding.nsmap "

void   * process_request( void   * soap) {
        pthread_detach(pthread_self());
        
if  ( soap_ssl_accept(( struct  soap  * ) soap)  !=  SOAP_OK )
                soap_print_fault((
struct  soap  * ) soap, stderr);
        
else
                soap_serve((
struct  soap  * ) soap);
        soap_end((
struct  soap  * ) soap);
        soap_free((
struct  soap  * ) soap);
        
return  NULL;
}

int  main( int  argc,  char   ** argv) {
        
if  ( argc  !=   2  ) {
                printf(
" Usage: %s port\n " , argv[ 0 ]);
                exit(
- 1 );
        }
        
int  port  =  atol(argv[ 1 ]);

        pthread_t tid;
        
struct  soap  * tsoap;
        
struct  soap soap;
        soap_init(
& soap);

        soap_ssl_init();
        
if  ( soap_ssl_server_context( & soap, SOAP_SSL_DEFAULT,  " server.pem " " password " " cacert.pem " , NULL,  " dh512.pem " , NULL, argv[ 0 ]) ) {
                soap_print_fault(
& soap, stderr);
                exit(
- 1 );
        }

        
int  m, s;
        
if  ( (m  =  soap_bind( & soap, NULL, port,  100 ))  <   0  ) {
                soap_print_fault(
& soap, stderr);
        }
        
else  {
                printf(
" Socket connect successfully: master socket = %d\n " , m);
                
int  i  =   0 ;
                
while  (  1  ) {
                        
if  ( (s  =  soap_accept( & soap))  <   0  ) {
                                soap_print_fault(
& soap, stderr);
                                
break ;
                        }
                        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);
                        tsoap 
=  soap_copy( & soap);
                        
if  (  ! tsoap ) {
                                soap_closesock(
& soap);
                                
continue ;
                        }
                        pthread_create(
& tid, NULL,  & process_request, ( void   * ) tsoap);
                }
        }
        soap_done(
& soap);
        
return   0 ;
}

int  __ns1__echo(
        
struct  soap  * soap,
        
struct  _ns1__echo  * request,
        
struct  _ns1__echoResponse  * response) {
        
if  (  ! soap -> userid  ||   ! soap -> passwd  ||  strcmp(soap -> userid,  " roy " ||  strcmp(soap -> passwd,  " liang " ) )
                
return   401 ;
        
int  len  =  strlen(request -> EchoMessage);
        response
-> Echo  =  ( char   * ) malloc( sizeof ( char *  (len  +   1 ));
        strcpy(response
-> Echo, request -> EchoMessage);
        
return  SOAP_OK;
}

 

 

保存为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 pointhttps://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处理即可,不再赘述。

 

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


http://blog.csdn.net/yui/archive/2010/08/17/5817771.aspx

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