使用gSOAP开发实例(8) -- 自定义header实现用户名令牌认证(Usernametoken Authentication)

上一节介绍了 怎样实现基本认证 (Basic Authentication ,以下简称 basic 方式 ) ,望文生义,也就是最简单的用户验证方式,本节稍微深入一些,介绍用户名令牌认证 (Usernametoken Authentication ,以下简称 usernametoken 方式 ) 。

 

Usernametoken 方式与 basic 方式不同的地方,在于后者会把用户名和密码以摘要 (digest) 的形式,置于 HTTP 信息头,而前者则把用户名以明文的形式、密码以明文或者摘要的形式,嵌入到一段 XML 文本中,再置于 SOAP 消息头当中。

 

如果使用 soapUI 调试客户端程序的话,会发现以下是 basic 方式发出的完整的 SOAP 消息:

POST https://test2.r-secure.com/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: test2.r-secure.com

Cookie: $Version=0; MSP2LB=test2.test2f02; $Path=/

 

   < soapenv:Header/>

   < soapenv:Body>

      < echo:echo>

         < echo:EchoMessage>hello

      < /echo:echo>

   < /soapenv:Body>

 

以下是 usernametoken 方式发出的完整的 SOAP 消息:

POST https://test.r-secure.com/4.0/services/SecureEcho HTTP/1.1

Content-Type: text/xml;charset=UTF-8

SOAPAction: ""

User-Agent: Jakarta Commons-HttpClient/3.1

Host: test.r-secure.com

Content-Length: xxx

 

   < soapenv:Header>

      < wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">

         < wsse:UsernameToken wsu:Id="UsernameToken-32870670" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">

            < wsse:Username>roy

            < wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">liang

            < wsse:Nonce>LX4gh+njbEtCNAtkWkXDYA==

            < wsu:Created>2010-08-11T06:02:25.874Z

         < /wsse:UsernameToken>

      < /wsse:Security>

      < echo:customerId>G06164

   < /soapenv:Header>

   < soapenv:Body>

      < echo:sendEcho>

         < echo:message>hello

      < /echo:sendEcho>

   < /soapenv:Body>

 

其中,加粗部分表示两者的主要区别,红字部分表示各自必不可少的元素。

 

由此可以看出, usernametoken 方式的特点,是在 SOAP 的 header 中加入一个 Security 标签,把 usernametoken 信息放在这个 Security 标签里。至于 header 里的另外一个 customerId 标签,是该应用自身的额外要求。

 

 

gSOAP 实现 basic 方式相对简单,不过实现 usernametoken 就比较复杂。 gSOAP 的用户指南推荐使用其自带的插件,在 samples/wsse 目录下的官方实例也是使用这个插件。但是,我个人认为这种方法比较累赘,自动生成的代码太多,不易看懂,而且似乎非常依赖于 wsdl 本身的写法。比如, samples/wsse 目录下的官方实例含有下列表示 SOAP header 的结构体,但是,在我实际开发的应用并没有自动产生,即使强行加上去,编译执行通过,运行的时候也出现了相当多的错误。

 

[cpp] view plain copy print ?
  1. struct SOAP_ENV__Header  
  2. {  
  3.         struct _wsse__Security *wsse__Security  
  4. };  
struct SOAP_ENV__Header{ struct _wsse__Security *wsse__Security};  

 

而且,从理论上讲, gSOAP 不过是一个框架,定义了从 SOAP 对象到 SOAP 消息,以及从 SOAP 消息到 SOAP 对象的序列化过程,并且提供了一套与之相适应的 API ,使用 gSOAP 开发不过是在其框架范围内调用其 API 编程。框架的弊端,可想而知,限制了灵活,也限制了方便,更限制了创新。所以,我们可以使用 gSOAP 编程,但是也许没有必要全部照搬,至少在这个案例中,就没有必要照搬。

 

我们应有的思路是,既然 customerId 可以直接写到 SOAP header 中,那么与之并列的、含有 usernametoken 的 Security 也可以直接写到 SOAP header 中,完全不需要依赖于 gSOAP 的 wsse 插件。

 

 

与上节一样,基于保密原则,本节案例采用的 wsdl 同样是经过裁剪和替换的,内容如下:

 

[xhtml] view plain copy print ?
  1.   
  2.   
  3.     
  4.   
  5.   
  6.   
  7.   
  8.   
  9.   
  10.   
  11.   
  12.   
  13.   
  14.   
  15.   
  16.   
  17.   
  18.   
  19.   
  20.   
  21.   
  22.   
  23.   
  24.   
  25.   
  26.   
  27.   
  28.   
  29.   
  30.   
  31.     
  32.     
  33.       
  34.       
  35.     
  36.     
  37.       
  38.       
  39.     
  40.     
  41.       
  42.       
  43.     
  44.     
  45.       
  46.       
  47.     
  48.     
  49.       
  50.       
  51.     
  52.     
  53.       
  54.       
  55.     
  56.     
  57.       
  58.         
  59.       
  60.         
  61.       
  62.       
  63.       
  64.         
  65.       
  66.         
  67.       
  68.       
  69.     
  70.     
  71.       
  72.       
  73.         
  74.         
  75.           
  76.           
  77.           
  78.         
  79.         
  80.           
  81.         
  82.       
  83.       
  84.         
  85.         
  86.           
  87.           
  88.           
  89.         
  90.         
  91.           
  92.         
  93.       
  94.     
  95.     
  96.       
  97.         
  98.       
  99.     
  100.   
  

 

在 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, "http://docs.oasis-open.org/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", "http://schemas.xmlsoap.org/soap/envelope/", "http://www.w3.org/*/soap-envelope", NULL},

        {"SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/", "http://www.w3.org/*/soap-encoding", NULL},

        {"xsi", "http://www.w3.org/2001/XMLSchema-instance", "http://www.w3.org/*/XMLSchema-instance", NULL},

        {"xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/*/XMLSchema", NULL},

        {"ns1", "http://echo.ws.rsecure.com", NULL, NULL},

        {"wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", NULL, NULL},

        {NULL, NULL, NULL, NULL}

};

 

 

存根程序修改完成,就可以开始编写客户端程序代码了。这个 web service 提供了两个接口,一个是 secure_echo ,也就是客户端送任意信息上来,服务端就返回相同的字符串,代码如下:

 

[cpp] view plain copy print ?
  1. #include "soapH.h"  
  2. #include "SecureEchoHttpBinding.nsmap"  
  3. int main(int argc, char **argv) {  
  4.     if ( argc != 5 && argc != 6 ) {  
  5.         printf("Usage: %s username password customer_id message [end_point]/n", argv[0]);  
  6.         exit(-1);  
  7.     }  
  8.     struct soap soap;  
  9.     soap_init(&soap);  
  10.     struct _ns1__sendEcho request;  
  11.     struct _ns1__sendEchoResponse response;  
  12.     soap_ssl_init();  
  13.     if ( soap_ssl_client_context(&soap, SOAP_SSL_NO_AUTHENTICATION, NULL, NULL, NULL, NULL, NULL) ) {  
  14.         soap_print_fault(&soap, stderr);  
  15.         exit(-1);  
  16.     }  
  17.     struct SOAP_ENV__Header header;  
  18.     header.wsse__username = argv[1];  
  19.     header.wsse__password = argv[2];  
  20.     header.ns1__customerId = argv[3];  
  21.     soap.header = &header;  
  22.     //soap_write_SOAP_ENV__Header(&soap, &header);  
  23.     request.message = argv[4];  
  24.     char *endpoint = NULL;  
  25.     if ( argc == 6 )  
  26.         endpoint = argv[5];  
  27.     printf("username    : %s/n", header.wsse__username);  
  28.     printf("password    : %s/n", header.wsse__password);  
  29.     printf("customer id : %s/n", header.ns1__customerId);  
  30.     printf("message     : %s/n", request.message);  
  31.     if ( endpoint )  
  32.         printf("end point : %s/n", endpoint);  
  33.     if ( soap_call___ns1__sendEcho(&soap, endpoint, NULL, &request, &response) == SOAP_OK ) {  
  34.         printf("%s/n", response.out);  
  35.     }  
  36.     else {  
  37.         soap_print_fault(&soap, stderr);  
  38.     }  
  39.     soap_destroy(&soap);  
  40.     soap_end(&soap);  
  41.     soap_done(&soap);  
  42.     return 0;  
  43. }  
#include "soapH.h"#include "SecureEchoHttpBinding.nsmap"int main(int argc, char **argv) { if ( argc != 5 && argc != 6 ) { printf("Usage: %s username password customer_id message [end_point]/n", argv[0]); exit(-1); } struct soap soap; soap_init(&soap); struct _ns1__sendEcho request; struct _ns1__sendEchoResponse 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); } struct SOAP_ENV__Header header; header.wsse__username = argv[1]; header.wsse__password = argv[2]; header.ns1__customerId = argv[3]; soap.header = &header; //soap_write_SOAP_ENV__Header(&soap, &header); request.message = argv[4]; char *endpoint = NULL; if ( argc == 6 ) endpoint = argv[5]; printf("username : %s/n", header.wsse__username); printf("password : %s/n", header.wsse__password); printf("customer id : %s/n", header.ns1__customerId); printf("message : %s/n", request.message); if ( endpoint ) printf("end point : %s/n", endpoint); if ( soap_call___ns1__sendEcho(&soap, endpoint, NULL, &request, &response) == SOAP_OK ) { printf("%s/n", response.out); } else { soap_print_fault(&soap, stderr); } soap_destroy(&soap); soap_end(&soap); soap_done(&soap); return 0;}  

 

另外一个是显示版本信息,代码如下:

 

[cpp] view plain copy print ?
  1. #include "soapH.h"  
  2. #include "SecureEchoHttpBinding.nsmap"  
  3. int main(int argc, char **argv) {  
  4.     if ( argc != 4 && argc != 5 ) {  
  5.         printf("Usage: %s username password customer_id [end_point]/n", argv[0]);  
  6.         exit(-1);  
  7.     }  
  8.     struct soap soap;  
  9.     soap_init(&soap);  
  10.     struct _ns1__showVersionInformation request;  
  11.     struct _ns1__showVersionInformationResponse response;  
  12.     soap_ssl_init();  
  13.     if ( soap_ssl_client_context(&soap, SOAP_SSL_NO_AUTHENTICATION, NULL, NULL, NULL, NULL, NULL) ) {  
  14.         soap_print_fault(&soap, stderr);  
  15.         exit(-1);  
  16.     }  
  17.     struct SOAP_ENV__Header header;  
  18.     header.wsse__username = argv[1];  
  19.     header.wsse__password = argv[2];  
  20.     header.ns1__customerId = argv[3];  
  21.     soap.header = &header;  
  22.     //soap_write_SOAP_ENV__Header(&soap, &header);  
  23.     char *endpoint = NULL;  
  24.     if ( argc == 5 )  
  25.         endpoint = argv[4];  
  26.     printf("username    : %s/n", header.wsse__username);  
  27.     printf("password    : %s/n", header.wsse__password);  
  28.     printf("customer id : %s/n", header.ns1__customerId);  
  29.     if ( endpoint )  
  30.         printf("end point : %s/n", endpoint);  
  31.     if ( soap_call___ns1__showVersionInformation(&soap, endpoint, NULL, &request, &response) == SOAP_OK ) {  
  32.         printf("%s/n", response.out);  
  33.     }  
  34.     else {  
  35.         soap_print_fault(&soap, stderr);  
  36.     }  
  37.     soap_destroy(&soap);  
  38.     soap_end(&soap);  
  39.     soap_done(&soap);  
  40.     return 0;  
  41. }  
#include "soapH.h"#include "SecureEchoHttpBinding.nsmap"int main(int argc, char **argv) { if ( argc != 4 && argc != 5 ) { printf("Usage: %s username password customer_id [end_point]/n", argv[0]); exit(-1); } struct soap soap; soap_init(&soap); struct _ns1__showVersionInformation request; struct _ns1__showVersionInformationResponse 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); } struct SOAP_ENV__Header header; header.wsse__username = argv[1]; header.wsse__password = argv[2]; header.ns1__customerId = argv[3]; soap.header = &header; //soap_write_SOAP_ENV__Header(&soap, &header); char *endpoint = NULL; if ( argc == 5 ) endpoint = argv[4]; printf("username : %s/n", header.wsse__username); printf("password : %s/n", header.wsse__password); printf("customer id : %s/n", header.ns1__customerId); if ( endpoint ) printf("end point : %s/n", endpoint); if ( soap_call___ns1__showVersionInformation(&soap, endpoint, NULL, &request, &response) == SOAP_OK ) { printf("%s/n", response.out); } else { soap_print_fault(&soap, stderr); } soap_destroy(&soap); soap_end(&soap); soap_done(&soap); return 0;}  

 

两个客户端程序都是一样的结构,仅仅是调用的接口不一样。与上一节的 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 接口都要编写:

 

[cpp] view plain copy print ?
  1. #include   
  2. #include "soapH.h"  
  3. #include "SecureEchoHttpBinding.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__sendEcho(  
  54.     struct soap *soap,  
  55.     struct _ns1__sendEcho *request,  
  56.     struct _ns1__sendEchoResponse *response) {  
  57.     if ( !soap->header || !soap->header->wsse__username || !soap->header->wsse__password || !soap->header->ns1__customerId  
  58.         || strcmp(soap->header->wsse__username, "roy") || strcmp(soap->header->wsse__password, "liang")  
  59.         || strcmp(soap->header->ns1__customerId, "G06164"))  
  60.         return 401;  
  61.     int len = strlen(request->message);  
  62.     response->out = (char *) malloc(sizeof(char) * (len + 1));  
  63.     strcpy(response->out, request->message);  
  64.     return SOAP_OK;  
  65. }  
  66. int __ns1__showVersionInformation(  
  67.     struct soap *soap,  
  68.     struct _ns1__showVersionInformation *request,  
  69.     struct _ns1__showVersionInformationResponse *response) {  
  70.     if ( !soap->header || !soap->header->wsse__username || !soap->header->wsse__password || !soap->header->ns1__customerId  
  71.         || strcmp(soap->header->wsse__username, "roy") || strcmp(soap->header->wsse__password, "liang")  
  72.         || strcmp(soap->header->ns1__customerId, "G06164"))  
  73.         return 401;  
  74.     response->out = (char *) malloc(sizeof(char) * 100);  
  75.     strcpy(response->out, "Username token (text) test server version 1.0");  
  76.     return SOAP_OK;  
  77. }  
#include #include "soapH.h"#include "SecureEchoHttpBinding.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__sendEcho( struct soap *soap, struct _ns1__sendEcho *request, struct _ns1__sendEchoResponse *response) { if ( !soap->header || !soap->header->wsse__username || !soap->header->wsse__password || !soap->header->ns1__customerId || strcmp(soap->header->wsse__username, "roy") || strcmp(soap->header->wsse__password, "liang") || strcmp(soap->header->ns1__customerId, "G06164")) return 401; int len = strlen(request->message); response->out = (char *) malloc(sizeof(char) * (len + 1)); strcpy(response->out, request->message); return SOAP_OK;}int __ns1__showVersionInformation( struct soap *soap, struct _ns1__showVersionInformation *request, struct _ns1__showVersionInformationResponse *response) { if ( !soap->header || !soap->header->wsse__username || !soap->header->wsse__password || !soap->header->ns1__customerId || strcmp(soap->header->wsse__username, "roy") || strcmp(soap->header->wsse__password, "liang") || strcmp(soap->header->ns1__customerId, "G06164")) return 401; response->out = (char *) malloc(sizeof(char) * 100); strcpy(response->out, "Username token (text) test server version 1.0"); return SOAP_OK;}  

 

服务端程序保存为 secure_echo_server.c ,编译命令是

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

 

运行测试,在 6883 端口启动服务端,然后执行客户端

-bash-3.2$ ./show_version roy liang G06164  

username    : roy

password    : liang

customer id : G06164

Username token (text) test server version 1.0

 

-bash-3.2$ ./secure_echo roy liang G06164 hello  

username    : roy

password    : liang

customer id : G06164

message     : hello

hello

 

以上就是 gSOAP 实现 Usernametoken 认证的方法,而且是通过自定义 SOAP header 实现的。个人认为,与使用 wsse 插件相比,这种方法更为简单直接。

 

另外,本案例的方法适用于以明文传送密码的情况,如果需要以摘要 (digest) 形式传送密码,请参考 plugin 目录 wsseapi.c 里面的 soap_wsse_add_UsernameTokenDigest 函数。

 

最后,感谢 Codejie's C++ Space 为本节的编写提供思路:

http://www.cppblog.com/codejie/archive/2010/04/07/89972.html

 

 

本文转自: http://blog.csdn.net/yui/article/details/5862411#comments

你可能感兴趣的:(OnVif)