📜 ⬆️ ⬇️

Creating a web service based on WSF / PHP with UsernameToken authentication and HTTPS

I received a task to deal with web services in principle and learn about the possibilities of writing a service (not a service client) in PHP, which can be accessed in accordance with WS and WS-Security standards. Compatibility with .NET clients was also required (in my case, working with WCF was enough).

After a brief googling, it became clear that neither the standard PHP SOAP library, nor the Zend Framework, nor anything else other than WSF / PHP, support WS-Security without a file, and the files found in a quick way offered only a specific solution, for example, UsernameToken with plaintext password (ideally, we need signatures, encryption, certificates, and so on and so forth).

This solution does not describe all the capabilities of the WSF / PHP framework, such as WSDL generation, since all this is described in the documentation and does not require any non-trivial solutions in terms of documentation. We will work with the version of the library 2.1.0 .
')

Training

First make sure that all the necessary components are installed. My service spun on Ubuntu 11.04, the client application was written in C # (in the listings below I will give its code and configuration). For WSF / PHP and service work you need:

Installation and configuration of all components are described in a variety of resources on the Internet, installation and configuration are described in the documentation (see the link in the introduction). Note that when configuring a PHP module, the wsf.home option points to the path where the library is installed (in my case it was /opt/wso2/wsf_c ).

Service

Our service is the only operation that squares the argument given to it and returns it. In brief, the content of the code: the function findSquare , which, in fact, performs our task; associating the operation name with the function name, declaring the security policy and creating a token (in this example, the login and password are hardcoded, but using the passwordCallback argument in the WSSecurityToken constructor (see API ), you can specify a function that can, for example, give it a login database password, and return it for further verification); Creation of a WSService instance with all parameters and call processing.

Suppose that the service is stored in the index.php file

 <?php function findSquare($integer) { $result = pow($integer, 2); return array("result"=>$result); } $operations = array( "squareInt" => "findSquare" ); // operations mapping $securityPolicy = new WSPolicy(file_get_contents('spolicy.xml')); // security policy $securityToken = new WSSecurityToken(array( "user"=>"god", "password"=>"iddqd", "passwordType"=>"PlainText", "ttl" => 100)); // security token $service = new WSService(array( "wsdl"=>"index.wsdl", "operations" => $operations, "serviceName" => "TestService", "policy"=>$securityPolicy, "securityToken"=>$securityToken )); // service instance $service->reply(); ?> 


We have two dependencies: spolicy.xml and index.wsdl . WSDL was generated by the WSF / PHP tool by adding the ?wsdl parameter to the service URL (how to generate WSDL with the correct types, etc., again, read the documentation: this point is described quite sensibly there).

Now go through spolicy.xml . This is an XML file written in accordance with the WS-SecurityPolicy specification. It was possible to describe everything in the arguments of the WSPolicy constructor, but for some time the UsernameToken required a signature from the client, which we do not need in this task, and the constructor arguments provide only the basic WS-SecurityPolicy capabilities. In addition, we need to announce that security issues lie on transport, that is, HTTPS.

 <wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> <wsp:ExactlyOne> <wsp:All> <sp:TransportBinding> <wsp:Policy> <sp:IncludeTimestamp/> </wsp:Policy> </sp:TransportBinding> <sp:SignedSupportingTokens> <wsp:Policy> <sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient"> <wsp:Policy> <sp:WssUsernameToken10 /> </wsp:Policy> </sp:UsernameToken> <sp:IncludeTimestamp/> </wsp:Policy> </sp:SignedSupportingTokens> </wsp:All> </wsp:ExactlyOne> </wsp:Policy> 


I will also index.wsdl ( domain.tld should be replaced with the domain or IP used by the service):
 <?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.wso2.org/php" xmlns:tnx="http://www.wso2.org/php/xsd" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:http="http://www.w3.org/2003/05/soap/bindings/HTTP/" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" targetNamespace="http://www.wso2.org/php"> <types> <xsd:schema elementFormDefault="qualified" targetNamespace="http://www.wso2.org/php/xsd"> <xsd:element name="squareInt"> <xsd:complexType> <xsd:sequence> <xsd:element name="integer" type="xsd:int"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="squareIntResponse"> <xsd:complexType> <xsd:sequence> <xsd:element name="result" type="xsd:int"/> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema> </types> <message name="squareInt"> <part name="parameters" element="tnx:squareInt"/> </message> <message name="squareIntResponse"> <part name="parameters" element="tnx:squareIntResponse"/> </message> <portType name="TestServicePortType"> <operation name="squareInt"> <input message="tns:squareInt"/> <output message="tns:squareIntResponse"/> </operation> </portType> <binding name="TestServiceSOAPBinding" type="tns:TestServicePortType"> <soap:binding xmlns="http://schemas.xmlsoap.org/wsdl/soap/" transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <operation xmlns:default="http://schemas.xmlsoap.org/wsdl/soap/" name="squareInt"> <soap:operation xmlns="http://schemas.xmlsoap.org/wsdl/soap/" soapAction="https://domain.tld:443/index.php/squareInt" style="document"/> <input xmlns:default="http://schemas.xmlsoap.org/wsdl/soap/"> <soap:body xmlns="http://schemas.xmlsoap.org/wsdl/soap/" use="literal"/> </input> <output xmlns:default="http://schemas.xmlsoap.org/wsdl/soap/"> <soap:body xmlns="http://schemas.xmlsoap.org/wsdl/soap/" use="literal"/> </output> </operation> </binding> <service name="TestService"> <port xmlns:default="http://schemas.xmlsoap.org/wsdl/soap/" name="TestServiceSOAPPort_Http" binding="tns:TestServiceSOAPBinding"> <soap:address xmlns="http://schemas.xmlsoap.org/wsdl/soap/" location="https://domain.tld:443/index.php"/> </port> </service> </definitions> 


The description of the service is completed. If everything is done correctly, then if you open the address of the service in the browser, a description of the deployed services and a list of their operations will open.

Customer

The client is a console application in C #, which knocks at the address specified in the configuration, and gets the result. Login, password, and transmitted value are hardcoded, but no one bothers you to correct this omission.

 internal class Program { // Methods private static void Main() { try { ServicePointManager.ServerCertificateValidationCallback = delegate (object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors errors) { return true; }; TestServicePortTypeClient client = new TestServicePortTypeClient(); client.ClientCredentials.UserName.UserName = "god"; client.ClientCredentials.UserName.Password = "iddqd"; Console.WriteLine(client.squareInt(5)); } catch (Exception exception) { Console.WriteLine(exception); } finally { Console.ReadKey(); } } } 


The configuration file ( domain.tld should be replaced with the domain or IP used by the service):
 <?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <bindings> <basicHttpBinding> <binding name="TestServiceSOAPBinding"> <security mode="TransportWithMessageCredential"> <transport clientCredentialType="None" proxyCredentialType="None" realm="" /> <message clientCredentialType="UserName" algorithmSuite="Default" /> </security> </binding> </basicHttpBinding> </bindings> <client> <endpoint address="https://domain.tld/index.php" binding="basicHttpBinding" bindingConfiguration="TestServiceSOAPBinding" contract="MyServiceReference.TestServicePortType" name="TestServiceSOAPPort_Http" /> </client> </system.serviceModel> </configuration> 


Total

As a result, 25 should appear in the console window, or, if you have corrected the client code in order to enter the initial value with your hands, the square of your number.

Source: https://habr.com/ru/post/132353/


All Articles