使用gSOAP开发实例(8) Phase 1 完结篇 自定义header实现用户名令牌认证(Usernametoken Authentication)

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

上一节介绍了 怎样实现基本认证 (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:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:echo="http://echo.rsecure.com/ECHO">

   <soapenv:Header/>

   <soapenv:Body>

      <echo:echo>

         <echo:EchoMessage>hello</echo:EchoMessage>

      </echo:echo>

   </soapenv:Body>

</soapenv:Envelope>

 

以下是 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:Envelope xmlns:echo="http://echo.ws.rsecure.com" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">

   <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:Username>

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

            <wsse:Nonce>LX4gh+njbEtCNAtkWkXDYA==</wsse:Nonce>

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

         </wsse:UsernameToken>

      </wsse:Security>

      <echo:customerId>G06164</echo:customerId>

   </soapenv:Header>

   <soapenv:Body>

      <echo:sendEcho>

         <echo:message>hello</echo:message>

      </echo:sendEcho>

   </soapenv:Body>

</soapenv:Envelope>

 

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

 

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

 

 

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

 

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

 

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

 

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

 

 

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

 

[xhtml]  view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <wsdl:definitions targetNamespace="http://echo.ws.rsecure.com" xmlns:soapenc12="http://www.w3.org/2003/05/soap-encoding" xmlns:tns="http://echo.ws.rsecure.com" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc11="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">  
  3.   <wsdl:types>  
  4. <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://echo.ws.rsecure.com">  
  5. <xsd:element name="sendEcho">  
  6. <xsd:complexType>  
  7. <xsd:sequence>  
  8. <xsd:element maxOccurs="1" minOccurs="1" name="message" type="xsd:string"/>  
  9. </xsd:sequence>  
  10. </xsd:complexType>  
  11. </xsd:element>  
  12. <xsd:element name="sendEchoResponse">  
  13. <xsd:complexType>  
  14. <xsd:sequence>  
  15. <xsd:element maxOccurs="1" minOccurs="1" name="out" type="xsd:string"/>  
  16. </xsd:sequence>  
  17. </xsd:complexType>  
  18. </xsd:element>  
  19. <xsd:element name="showVersionInformation">  
  20. <xsd:complexType/>  
  21. </xsd:element>  
  22. <xsd:element name="showVersionInformationResponse">  
  23. <xsd:complexType>  
  24. <xsd:sequence>  
  25. <xsd:element maxOccurs="1" minOccurs="1" name="out" type="xsd:string"/>  
  26. </xsd:sequence>  
  27. </xsd:complexType>  
  28. </xsd:element>  
  29. <xsd:element name="customerId" type="xsd:string"/>  
  30. </xsd:schema>  
  31.   </wsdl:types>  
  32.   <wsdl:message name="sendEchoRequestHeaders">  
  33.     <wsdl:part name="customerId" element="tns:customerId">  
  34.     </wsdl:part>  
  35.   </wsdl:message>  
  36.   <wsdl:message name="showVersionInformationRequestHeaders">  
  37.     <wsdl:part name="customerId" element="tns:customerId">  
  38.     </wsdl:part>  
  39.   </wsdl:message>  
  40.   <wsdl:message name="sendEchoResponse">  
  41.     <wsdl:part name="parameters" element="tns:sendEchoResponse">  
  42.     </wsdl:part>  
  43.   </wsdl:message>  
  44.   <wsdl:message name="showVersionInformationRequest">  
  45.     <wsdl:part name="parameters" element="tns:showVersionInformation">  
  46.     </wsdl:part>  
  47.   </wsdl:message>  
  48.   <wsdl:message name="sendEchoRequest">  
  49.     <wsdl:part name="parameters" element="tns:sendEcho">  
  50.     </wsdl:part>  
  51.   </wsdl:message>  
  52.   <wsdl:message name="showVersionInformationResponse">  
  53.     <wsdl:part name="parameters" element="tns:showVersionInformationResponse">  
  54.     </wsdl:part>  
  55.   </wsdl:message>  
  56.   <wsdl:portType name="SecureEcho">  
  57.     <wsdl:operation name="sendEcho">  
  58.       <wsdl:input name="sendEchoRequest" message="tns:sendEchoRequest">  
  59.     </wsdl:input>  
  60.       <wsdl:output name="sendEchoResponse" message="tns:sendEchoResponse">  
  61.     </wsdl:output>  
  62.     </wsdl:operation>  
  63.     <wsdl:operation name="showVersionInformation">  
  64.       <wsdl:input name="showVersionInformationRequest" message="tns:showVersionInformationRequest">  
  65.     </wsdl:input>  
  66.       <wsdl:output name="showVersionInformationResponse" message="tns:showVersionInformationResponse">  
  67.     </wsdl:output>  
  68.     </wsdl:operation>  
  69.   </wsdl:portType>  
  70.   <wsdl:binding name="SecureEchoHttpBinding" type="tns:SecureEcho">  
  71.     <wsdlsoap:binding style="document" mce_style="document" transport="http://schemas.xmlsoap.org/soap/http"/>  
  72.     <wsdl:operation name="sendEcho">  
  73.       <wsdlsoap:operation soapAction=""/>  
  74.       <wsdl:input name="sendEchoRequest">  
  75.         <wsdlsoap:body use="literal"/>  
  76.         <wsdlsoap:header message="tns:sendEchoRequestHeaders" part="customerId" use="literal">  
  77.         </wsdlsoap:header>  
  78.       </wsdl:input>  
  79.       <wsdl:output name="sendEchoResponse">  
  80.         <wsdlsoap:body use="literal"/>  
  81.       </wsdl:output>  
  82.     </wsdl:operation>  
  83.     <wsdl:operation name="showVersionInformation">  
  84.       <wsdlsoap:operation soapAction=""/>  
  85.       <wsdl:input name="showVersionInformationRequest">  
  86.         <wsdlsoap:body use="literal"/>  
  87.         <wsdlsoap:header message="tns:showVersionInformationRequestHeaders" part="customerId" use="literal">  
  88.         </wsdlsoap:header>  
  89.       </wsdl:input>  
  90.       <wsdl:output name="showVersionInformationResponse">  
  91.         <wsdlsoap:body use="literal"/>  
  92.       </wsdl:output>  
  93.     </wsdl:operation>  
  94.   </wsdl:binding>  
  95.   <wsdl:service name="SecureEcho">  
  96.     <wsdl:port name="SecureEchoHttpPort" binding="tns:SecureEchoHttpBinding">  
  97.       <wsdlsoap:address location="https://localhost:6883"/>  
  98.     </wsdl:port>  
  99.   </wsdl:service>  
  100. </wsdl:definitions>  

 

在 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
  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. }  

 

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

 

[cpp]  view plain copy
  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. }  

 

两个客户端程序都是一样的结构,仅仅是调用的接口不一样。与上一节的 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
  1. #include <pthread.h>  
  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. }  

 

服务端程序保存为 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

你可能感兴趣的:(使用gSOAP开发实例(8) Phase 1 完结篇 自定义header实现用户名令牌认证(Usernametoken Authentication))