PHP5 WebServices vs MS.NET vs Java vs Java Mobile



Podobne dokumenty
Katedra Architektury Systemów Komputerowych Wydział Elektroniki, Telekomunikacji i Informatyki Politechniki Gdańskiej

Narzędzia i aplikacje Java EE. Usługi sieciowe Paweł Czarnul pczarnul@eti.pg.gda.pl

Technologie internetowe laboratorium nr 4. Instalacja i uruchomienie usługi sieciowej w serwerze Tomcat/AXIS

Dostęp do komponentów EJB przez usługi Web Services

Integracja Obieg Dokumentów - GiS Spis treści

Katalog książek cz. 3: Web Service

Materiały oryginalne: ZAWWW-2st1.2-l11.tresc-1.0kolor.pdf. Materiały poprawione

Ćwiczenie 1. Kolejki IBM Message Queue (MQ)

Simple Object Access Protocol

SOAP. Autor: Piotr Sobczak

Wprowadzenie do technologii Web Services: SOAP, WSDL i UDDI

76.Struktura oprogramowania rozproszonego.

Sposoby tworzenia projektu zawierającego aplet w środowisku NetBeans. Metody zabezpieczenia komputera użytkownika przed działaniem apletu.

Aplikacje RMI

Tworzenie i wykorzystanie usług

Wywoływanie metod zdalnych

Tworzenie i wykorzystanie usług sieciowych

PHP: bloki kodu, tablice, obiekty i formularze

Dokumentacja SMS przez FTP

Wywoływanie metod zdalnych

1 Wprowadzenie do J2EE

WINDOWS Instalacja serwera WWW na systemie Windows XP, 7, 8.

Architektury Usług Internetowych. Laboratorium 2. Usługi sieciowe

Aplikacje internetowe i rozproszone - laboratorium

Komunikacja i wymiana danych

Zaawansowane aplikacje internetowe - laboratorium

1. Uruchomić i skonfigurować środowisko tworzenia aplikacji i serwer aplikacji.

Usługi WWW. dr Zbigniew Lipiński Instytut Matematyki i Informatyki ul. Oleska Opole zlipinski@math.uni.opole.pl

Programowanie komponentowe. Przykład 1 Bezpieczeństwo wg The Java EE 5 Tutorial Autor: Zofia Kruczkiewicz

Programowanie Komponentowe WebAPI

Podstawy technologii WWW

Wprowadzenie do Doctrine ORM

Wstęp. Skąd pobrać program do obsługi FTP? Logowanie

Remote Method Invocation 17 listopada 2010

Dokumentacja smsapi wersja 1.4

Instrukcja dla użytkowników Windows Vista Certyfikat Certum Basic ID

Rozdział ten przedstawia jeden ze sposobów implementacji usług sieciowych XML i aplikacji klienckich w PHP. Oprogramowanie

Certyfikat Certum Basic ID. Instrukcja dla użytkowników Windows Vista. wersja 1.3 UNIZETO TECHNOLOGIES SA

Podejście obiektowe do budowy systemów rozproszonych

Java RMI. Dariusz Wawrzyniak 1. Podejście obiektowe do budowy systemów rozproszonych. obiekt. interfejs. kliencka. sieć

1 LINQ. Zaawansowane programowanie internetowe Instrukcja nr 1

Podejście obiektowe do budowy systemów rozproszonych

Wprowadzenie do projektu QualitySpy

Dokumentacja do API Javy.

Git, Bitbucket, IntelliJ IDEA

Spring Web MVC, Spring DI

PHP 5 język obiektowy

Zdalne wywołanie metod - koncepcja. Oprogramowanie systemów równoległych i rozproszonych Wykład 7. Rodzaje obiektów. Odniesienie do obiektu

Sesje, ciasteczka, wyjątki. Ciasteczka w PHP. Zastosowanie cookies. Sprawdzanie obecności ciasteczka

Kontrola sesji w PHP HTTP jest protokołem bezstanowym (ang. stateless) nie utrzymuje stanu między dwoma transakcjami. Kontrola sesji służy do

Java RMI. Dariusz Wawrzyniak 1. Podejście obiektowe do budowy systemów rozproszonych. obiekt. interfejs. kliencka. sieć

Instrukcja konfigurowania poczty Exchange dla klienta pocztowego użytkowanego poza siecią uczelnianą SGH.

Gatesms.eu Mobilne Rozwiązania dla biznesu

Remote Method Invocation 17 listopada Dariusz Wawrzyniak (IIPP) 1

Projektowanie oprogramowania. Warstwa integracji z bazą danych oparta na technologii ORM Platforma Java EE Autor: Zofia Kruczkiewicz

Zaawansowane aplikacje WWW - laboratorium

Michał Jankowski. Remoting w.net 2.0

Wykład 5: PHP: praca z bazą danych MySQL

Klasy i obiekty cz II

Programowanie współbieżne i rozproszone

Obsługa poczty elektronicznej w domenie emeritus.ue.poznan.pl

Programowanie obiektowe

SPECYFIKACJA WYMIANY DANYCH POMIĘDZY PROGRAMEM KS-APTEKA WINDOWS I SKLEPEM INTERNETOWYM FIRMY ZEWNĘTRZNEJ

Konfiguracja konta pocztowego w Thunderbird

Wdrożenie modułu płatności eservice. dla systemu Zen Cart

Obiektowe programowanie rozproszone Java RMI. Krzysztof Banaś Systemy rozproszone 1

Oprogramowanie systemów równoległych i rozproszonych Wykład 7

Nr: 12. Tytuł: UDOSTĘPNIANIE DANYCH O SPRAWACH KLIENTOM KANCELARII NA ZEWNĘTRZNYCH SERWERACH WWW. Data modyfikacji:

elektroniczna Platforma Usług Administracji Publicznej

Wątek - definicja. Wykorzystanie kilku rdzeni procesora jednocześnie Zrównoleglenie obliczeń Jednoczesna obsługa ekranu i procesu obliczeniowego

Laboratorium 7 Blog: dodawanie i edycja wpisów

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

Programowanie Multimediów. Programowanie Multimediów JAVA. wprowadzenie do programowania (3/3) [1]

Programowanie w Sieci Internet Blok 2 - PHP. Kraków, 09 listopada 2012 mgr Piotr Rytko Wydział Matematyki i Informatyki

Przykład połączenie z bazą danych

Aplikacje WWW - laboratorium

JAX-RS czyli REST w Javie. Adam Kędziora

Laboratorium 10 - Web Services

Rozdział 4 KLASY, OBIEKTY, METODY

Aplikacje RMI Lab4

ASP.NET MVC. Podstawy. Zaawansowane programowanie internetowe Instrukcja nr 3

Wyjątki (exceptions)

Web Services (SOAP) Ćwiczenie 1

ESDI. WebService. Wersja 1.2. Strona 1

Współpraca z platformą Emp@tia. dokumentacja techniczna

Programowanie komponentowe

Instrukcja 2 Laboratorium z Podstaw Inżynierii Oprogramowania

Wdrożenie modułu płatności eservice. dla systemu oscommerce 2.3.x

AXIS2 - tworzenie usługi sieciowej i klienta Axis Data Binding. dr inż. Juliusz Mikoda mgr inż. Anna Wawszczak

Czym są właściwości. Poprawne projektowanie klas

4. Podstawowa konfiguracja

Skrócona instrukcja korzystania z Platformy Zdalnej Edukacji w Gliwickiej Wyższej Szkole Przedsiębiorczości

Java. język programowania obiektowego. Programowanie w językach wysokiego poziomu. mgr inż. Anna Wawszczak

Remote Method Invocation 17 listopada rozproszonych. Dariusz Wawrzyniak (IIPP) 1

Badania poziomu bezpieczeństwa portalu dostępowego do infrastruktury projektu PL-Grid

Laboratorium - Poznawanie FTP

Obiektowy PHP. Czym jest obiekt? Definicja klasy. Składowe klasy pola i metody

1. Czynności przygotowujące aplikację działającą na platformie Java SE Biblioteka5 (należy ją pobrać z załącznika z p.1)

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Transkrypt:

Narzędzia PHP5 WebServices vs MS.NET vs Java vs Java Mobile Łukasz Budnik Stopień trudności: W tym artykule pokażemy, jak stworzyć prostą usługę w PHP5, jak się z nią połączyć. Następnie pokażemy, jak należy łączyć się z usługami sieciowymi MS.NET, Jakarta AXIS (Java). Pokażemy też jak zaimplementować usługę z Security Extension udostępnianą po HTTPS w PHP5, jak napisać klienta PHP5, który będzie się łączył z taką usługą i weryfikował oraz zabezpieczał połączenie własnym certyfikatem i kluczem. Pokażemy również, jak połączyć się z usługą PHP5 z komórki (Java, w dodatku Java mobile), a także, jak mapować obiekty złożone z PHP5 do MS.NET. W PHP5 pojawił się bardzo interesujący moduł SOAP. SOAP (ang. Simple Object Access Protocol) zapewnia nam możliwość tworzenia własnych oraz wywoływania zdalnych usług sieciowych, rozsianych po całym Internecie. Implementacja SOAP w PHP5 nie zawiera wszystkich rozszerzeń protokołu SOAP, ale sami możemy je dopisać, ponieważ zarówno klasy serwerów, jak i klientów, możemy rozszerzyć (klasy nie są typu final). Web Services, protokół SOAP Ale zacznijmy od początku. Web Services to systemy, które są projektowane tak, aby komputery o różnych architekturach, różnych systemach operacyjnych działające w rozproszonym środowisku mogły wywoływać zdalnie swoje pewne operacje poprzez sieć Internet. Najczęściej wykorzystują one jako warstwę transportową HTTP (lub HTTPS). Można powiedzieć, że Web Services to nowa wersja CORBA. SOAP jest to protokół, poprzez który realizowane są wszystkie operacje Web Services. SOAP bazuje na XML, jest więc bardzo prostym protokołem, do którego obsługi wystarczy nam zwykły par- Co należy wiedzieć... Powinieneś posiadać dobrą znajomość PHP5, znać ogólne zasady współpracy i projektowania systemów rozproszonych na różnych systemach. Mile widziana znajomość podstaw SOAP i WSDL. Co obiecujemy... Nauczysz się pisać własne usługi sieciowe, dowiesz się, jak rozszerzyć domyślne klasy PHP5 klienta i serwera SAOP. Nauczysz się zabezpieczać swoje usługi z rozszerzeniem WS Security Enhancements i HTTPS. Nauczysz się, jak mapować złożone obiekty i przesyłać je pomiędzy różnymi platformami technologicznymi. Dowiesz się, jak połączyć się z usługami sieciowym MS.NET, Jakarta AXIS. 52 www.phpsolmag.org

SOAP w PHP5 Narzędzia Rysunek 1. Rozbudowana koperta SAOP WS Security Enhancements Listing 1. Koperta SOAP służąca do wywołania zdalnej usługi PobierzArtykul <SOAP-ENV:Envelope SOAP-ENV:encodingStyle= "http://schemas.xmlsoap.org/soap/encoding/"> <SOAP-ENV:Body> <!-- nazwa metody --> <ns1:pobierzartykul> <!-- parametry... tutaj parametr o nazwie id, typu xsd:int o wartości 1212 --> <id xsi:type="xsd:int">1212</id> </ns1:pobierzartykul> </SOAP-ENV:Body> </SOAP-ENV:Envelope> Listing 2. Koperta SOAP będąca odpowiedzią na wywołanie usługi PobierzArtykul <SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/ encoding/"> <SOAP-ENV:Body> <!-- wynik działania PobierzArtykul --> <ns1:pobierzartykulresponse> <!-- typem zwracanym jest xsd:string o podanej zawartości --> <PobierzArtykulReturn xsi:type="xsd:string"> zalozmy, ze przeszukalismy baze i to jest ten jedyny artykul </PobierzArtykulReturn> </ns1:pobierzartykulresponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope> Listing 3. Wiązanie ProsteOperacjeSoapBinding <wsdl:binding name="prosteoperacjesoapbinding" type="impl:prosteoperacje"> <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="pobierzartykul"> <wsdlsoap:operation soapaction=""/> <wsdl:input name="pobierzartykulrequest"> <wsdlsoap:body encodingstyle="http://schemas.xmlsoap.org/soap/ encoding/" namespace="http://t:81/soap/article/ ProsteOperacjeWS.php" use="encoded"/> </wsdl:input> <wsdl:output name="pobierzartykulresponse"> <wsdlsoap:body encodingstyle="http://schemas.xmlsoap.org/soap/ encoding/" namespace="http://t:81/soap/article/ ProsteOperacjeWS.php" use="encoded"/> </wsdl:output> </wsdl:operation> </wsdl:binding> ser dokumentów XML. SOAP jest standardem opracowanym przez W3C. Jego implementacje znajdziemy na każdej platformie technologicznej, w tym takich gigantów jak MS.NET, Java czy Java w wersji na urządzenia przenośne. Jak działa SOAP? Klient podczas zdalnego wywoływania metod wymienia z serwerem dokumenty XML o ściśle zdefiniowanej strukturze, tak zwane koperty. Koperta z kolei zawiera dwa elementy: Header (nieobowiązkowy) oraz Body (obowiązkowy). W Header można wstawiać dodatkowe informacje typu data, miejsce skąd wywołujemy usługę, login, hasło. W Body klient umieszcza informacje o metodzie, którą chce wywołać oraz ewentualne jej parametry. Serwer odpowiada również kopertą, w której może być nagłówek (Header) i zawsze musi być ciało (Body), w której znajdują się dane, które są wynikiem wykonania zdalnej procedury. Podstawowy schemat żądania został przedstawiony na Listingu 1. Natomiast odpowiedź została przedstawiona na Listingu 2. Nie musimy znać dokładnych struktur kopert wymienianych pomiędzy klientem a serwerem, ponieważ są one tworzone automatycznie i nie potrzebujemy w nie ingerować, jeśli nie chcemy np. rozszerzać domyślnych implementacji SOAP w PHP5. W późniejszej części artykułu, pokażę, jak rozszerzać klasy PHP5 i modyfikować takie koperty. Przykład koperty SOAP żądania klienta z security extension możemy zobaczyć na Rysunku 1. WSDL Web Services Definition Language WSDL to najtrudniejsza część związana z Web Services. WSDL to język, w którym definiujemy naszą usługę tak, aby inne systemy mogły z niej korzystać. WSDL składa się z 5 najważniejszych części: <types> definicje typów złożonych, które nie są obsługiwane przez XSD, jeśli nie używamy tablic, czy własnych klas kopertowych, nie musimy definiować nic, <message> definicje wiadomości, definiujemy tu z jakich typów składają się dana wiadomość, jeśli z typów złożonych, muszą one być wcześniej zdefiniowane w <types>, www.phpsolmag.org 53

Narzędzia SOAP w PHP5 Listing 4. WSDL definiujący przykładową operację PobierzArtykul <?xml version="1.0" encoding="utf-8"?> <wsdl:definitions targetnamespace="http://t:81/soap/article/prosteoperacjews.php" xmlns:impl="http://t:81/soap/article/prosteoperacjews.php" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/xmlschema"> <wsdl:message name="pobierzartykulresponse"> <wsdl:part name="pobierzartykulreturn" type="xsd:string"/> </wsdl:message> <wsdl:message name="pobierzartykulrequest"> <wsdl:part name="id" type="xsd:int"/> </wsdl:message> <wsdl:porttype name="prosteoperacje"> <wsdl:operation name="pobierzartykul" parameterorder="id"> <wsdl:input message="impl:pobierzartykulrequest" name="pobierzartykulrequest"/> <wsdl:output message="impl:pobierzartykulresponse" name="pobierzartykulresponse"/> </wsdl:operation> </wsdl:porttype> <wsdl:binding name="prosteoperacjesoapbinding" type="impl:prosteoperacje"> <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="pobierzartykul"> <wsdlsoap:operation soapaction=""/> <wsdl:input name="pobierzartykulrequest"> <wsdlsoap:body encodingstyle="http://schemas.xmlsoap.org/soap/ encoding/" namespace="http://defaultnamespace" use="encoded"/> </wsdl:input> <wsdl:output name="pobierzartykulresponse"> <wsdlsoap:body encodingstyle="http://schemas.xmlsoap.org/soap/ encoding/" namespace="http://t:81/soap/article/ ProsteOperacjeWS.php" use="encoded"/> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="prosteoperacjeservice"> <wsdl:port binding="impl:prosteoperacjesoapbinding" name="prosteoperacje"> <wsdlsoap:address location="http://t:81/soap/article/ ProsteOperacjeWS.php"/> </wsdl:port> </wsdl:service> </wsdl:definitions> Listing 5. Kod klasy implementującej interfejs naszej usługi sieciowej // w normalnych warunkach klasa ProsteOperacje // łączyła by się z bazą danych i pobierała artykułu // dodatkowo ciągi znaków przesyłane SOAP muszą być // zakodowane w UTF-8, używam do tego prostych funkcji konwertujących require_once ('Strings.inc.php' class ProsteOperacje { function PobierzNajnowszyArtykul() { return Strings::ConvertFromISOToUTF("załóżmy, że to jest ten artykuł" function PobierzArtykul($id) { return Strings::ConvertFromISOToUTF("zalozmy, ze przeszukalismy baze i to jest ten jedyny artykul" function Pobierz10Najnowszych($kategoria) { return array ( Strings::ConvertFromISOToUTF('pierwszy artykuł'), Strings::ConvertFromISOToUTF('drugi artykuł'), Strings::ConvertFromISOToUTF('kolejny artykuł') Listing 6. Plik ProsteOperacjeWS.php // plik z naszą klasą ProsteOperacje require_once 'ProsteOperacje. inc.php'; // parametrem konstruktora // SoapServer jest adres do pliku // WSDL $server = new SoapServer ('ProsteOperacje.wsdl' // klasa implementująca porttype $server->setclass('prosteoperacje' // przekazujemy sterowanie do PHP5 // to wszystko $server->handle( <porttype> definiuje zbiór operacji udostępnianych na zewnątrz, jest odpowiednikiem interfejsu klas, operacje definiuje w dzieciach <operation>, które definiują operacje, pobierają na wejściu pewną wiadomość i zwracają wiadomość; wiadomości muszą być definiowane w <message>, <binding> wiążę operacje zdefiniowane w porttype z konkretnym protokołem (najczęściej HTTP/HTTPS) oraz zawiera informacje na temat kodowania parametrów/odpowiedzi konkretnych operacji, <service> wiąże wcześniej zdefiniowany binding z konkretnym adresem, pod którym dostępna będzie nasza usługa. Z tak opisanej usługi klient może dowiedzieć się wszystkiego, czego potrzebuje, aby wywołać dowolną metodę. Istnieje wiele pakietów typu WSDL2Java, które generują kody klientów w Java-ie, gotowe do użycia i wywołania zdalnego serwisu, MS.NET sam tworzy referencje i wiązania obiektów, również w PHP5 w kliencie SOAP wystarczy podać adres do pliku WSDL, aby można było korzystać ze wszystkich usług zdefiniowanych w Web Services. WSDL może na pierwszy rzut oka wydawać się bardzo trudnym. Nic jednak bardziej mylnego, jest wręcz dziecinnie prosty. Zobrazuje to nasz pierwszy przykład. Pierwsza usługa Web Service PHP5 WSDL Zdefiniujmy klasę, której publiczne metody będą operacjami udostępnianymi przez naszą usługę. Poniżej pokazuję, 54 www.phpsolmag.org

SOAP w PHP5 Narzędzia jak krok po kroku zdefiniować operację, której sygnaturę można zapisać jako: String PobierzArtykul(int id). Jak widzimy używamy tu typów xsd: string oraz xsd:int nie potrzebujemy więc tworzyć żadnych innych typów. Przechodzimy od razu do elementów <message>, zwyczajowo przyjęło się aby nazwy wiadomości składały się z nazwy operacji, plus Request dla wiadomości wysyłanej przez klienta (żądania), oraz Response dla wiadomości wysyłanej przez serwer. Piszemy zatem: Rysunek 2. Jeśli przeglądając usługę przez przeglądarkę zobaczymy błąd przewrotnie znaczy to, że usługa działa, tylko nie ma danych POST <wsdl:message name="pobierz ArtykulResponse"> <wsdl:part name="pobierz ArtykulReturn" type="xsd:string"/> </wsdl:message> <wsdl:message name="pobierz ArtykulRequest"> <wsdl:part name="id" type="xsd:int"/> </wsdl:message> Następnym krokiem jest definicja elementu <porttype> i <operation> a w niej elementy input oraz output, które wskazują na zdefiniowane przed chwilą wiadomości. Nazywamy porttype jako ProsteOperacje, oraz operation jako PobierzArtykuł. Jako atrybut message elementów input oraz output podajemy odpowiednie wiadomości z przedrostkiem impl: (namespace impl). Rysunek 3. Każda usługa powinna przedstawić się podając swój WSDL po dodaniu?wsdl do adresu usługi <wsdl:porttype name="proste Operacje"> <wsdl:operation name="pobierzartykul"> <wsdl:input message="impl:pobierz ArtykulRequest" name="pobierzartykul Request"/> <wsdl:output message="impl:pobierz ArtykulResponse"name="PobierzArtykul Response"/> </wsdl:operation> </wsdl:porttype> Rysunek 4. Pierwsze zdalne wywołanie naszej usługi sieciowej Mając już zdefiniowany interfejs o nazwie ProsteOperacje musimy związać go z protokołem, stylem wywoływania zdalnych metod, oraz określić, czy i jak kodowane mają być wiadomości. Definiuję warstwę transportową jako HTTP, styl RPC (ang. remote procedure call) oraz definiuję kodowanie encoded dla wiadomości. Jako atrybut type podaję nazwę porttype (wartość atrybutu name dla elementu porttype z przedrostkiem impl:). Wiązanie na- www.phpsolmag.org 55

Narzędzia SOAP w PHP5 Listing 7. Pełny kod klienta PHP5 łączącego się z usługą sieciową, wraz ze śledzeniem wywołań i obsługą zdalnych wyjątków require_once ('Strings.inc.php' $client = new SoapClient( 'http://t:81/soap/article/prosteoperacjews.php?wsdl', array('trace' => 1, 'exceptions' => 1) try { echo '<pre>'; var_dump(strings::convertfromutftoiso($client-> PobierzNajnowszyArtykul()) $id = 1212; var_dump(strings::convertfromutftoiso($client->pobierzartykul($id)) $artykuly = $client->pobierz10najnowszych( for($i = 0; $i < count($artykuly $i++) { $artykuly[$i] = Strings::ConvertFromUTFToIso($artykuly[$i] var_dump($artykuly catch (SoapFault $e) { echo $e->getmessage( // wyswietlmy zawartosc XML po ludzku echo wordwrap(htmlentities($client-> getlastrequest()) echo wordwrap(htmlentities($client-> getlastresponse()) echo '</pre>'; Rysunek 5. Dodanie Web Reference do projektu C#.NET zywam ProsteOperacjeSoapBinding, które przedstawia Listing 3. Ostatnim elementem jest powiązanie wiązania o nazwie ProsteOperacjeSoap- Binding z adresem pod którym będzie dostępna usługa. W elemencie <port> dla atrybutu binding przypisuję nazwę wiązania z prefiksem impl:. <wsdl:service name="prosteoperacjeservice"> <wsdl:port binding="impl: ProsteOperacjeSoapBinding" name="prosteoperacje"> <wsdlsoap:address location= "http://t:81/soap/article/ ProsteOperacjeWS.php"/> </wsdl:port> </wsdl:service> Listing 8. Tworzenie Thread t = new Thread() public void run() { String url = "http://t:81/soap/article/prosteoperacjews.php"; HttpTransport transport = new HttpTransport(url, "ProsteOperacje" SoapObject request = new SoapObject(url, "PobierzArtykul" request.addproperty("id", Integer.valueOf(idArtykulu.getString()) try { odpowiedz.settext("\n" + ((String)transport.call(request)) catch (IOException ex) { ex.printstacktrace( ; t.start( Pełny WSDL jaki przed chwilką stworzyliśmy możemy zobaczyć na Listingu 4. Pierwsza usługa Web Service PHP5 serwer Jeśli mamy już WSDL, teraz pójdzie nam już tylko z górki. Implementacja SO- AP w PHP5 jest bardzo prosta w użyciu. Jak pokazałem na Listingu 5, usługa jest dostępna pod adresem: http://t:81/soap/ article/prosteoperacjews.php (oczywiście zmieniając serwer, port i lokację skryptu należy pamiętać o modyfikacji WSDL element <address> i parametr location). Rysunek 6. Usługa sieciowa napisana w PHP5 wywołana z poziomu Java SE 56 www.phpsolmag.org

SOAP w PHP5 Narzędzia Załóżmy, że mamy klasę Proste- Operacje, taką jak pokazana na Listingu 4, którą chcemy udostępnić jako usługę sieciową. Normalnie, wszystkie artykuły powinny być wyciągane z bazy danych, ale my w przykładzie użyjemy danych wprowadzonych na sztywno. Listing 6 prezentuje jak powinien wyglądać plik ProsteOperacjeWS.php. W tym momencie, jeśli połączymy się z naszą usługą np. przez przeglądarkę WWW, dostaniemy komunikat, że nie znaleziono danych POST (wiadomości SOAP wrzucane są w ciało wiadomości HTTP a zatem muszą używać metody POST) zobacz Rysunek 2. Wiele silników Web Services próbuje w momencie łączenia się do usługi pobrać WSDL automatycznie. Przyjęło się, że WSDL powinien być zwracany zawsze, jeśli QUERY_STRING jest równy '?wsdl', zatem dopiszmy na samym początku pliku ProsteOperacjeWS.php: if (0 == strcmp($_server ['QUERY_STRING'], '?wsdl')) { header('content-type: text/xml' readfile('prosteoperacje.wsdl' die( Teraz jeśli w przeglądarce podamy adres: http://t:81/soap/article/prosteoperacjews. php?wsdl zobaczymy definicję WSDL naszej usługi tak, jak jest to pokazane na Rysunku 3. Pierwsza usługa Web Service PHP5 klient Aby przetestować naszą usługę, należy napisać klienta. Napisać klienta podobnie jak serwer jest bardzo prosto, a nawet prościej, bo nie musimy tworzyć żadnego WSDL. Aby wykonać dowolną metodę zdalną w wersji minimalistycznej potrzebujemy 2 linii: $client = new SoapClient('http:// t:81/soap/article/prosteoperacjews. php?wsdl' Listing 9. Przykładowa usługa sieciowa napisana w C# namespace WebServiceTest { public class WebService : System.Web.Services.WebService { [WebMethod] public string getstring(){ return "Jestem łańcuchem"; [WebMethod] public double getdouble() { return -123123.23123d; [WebMethod] var_dump(strings::convertfromutftoiso ($client->pobierznajnowszyartykul()) Jak widzimy, użyłem tu ponownie funkcji konwertującej. Wszystko, co jest wysyłane SOAP musi być w UTF-8, większość stron polskojęzycznych używa jednak kodowania ISO-8859-2, użyłem więc prostej funkcji konwertującej polskie znaki diakrytyczne z UTF-8 do ISO. Jeśli spojrzymy na Listing 5, zauważymy, że SoapClient posiada również drugi argument, który domyślnie jest NULL. Są to opcje, bardzo przydatne np. podczas testowania. Jeśli włączymy flagę trace, będziemy mieli do dyspozycji dwie metody: $client-> getlastrequest( $client-> getlastresponse( które zwracają odpowiednio: ostatnie żądanie wysłane do serwera i ostatnią odpowiedź. Jeśli włączymy flagę exceptions, PHP5 będzie reagował na wyjątki, które zostały wyrzucone na zdalnej maszynie, dlatego też na Listingu dodałem blok try/catch wokół wywołania metod na obiekcie $client. Wynik naszego pierwszego zdalnego wywołania przedstawiony jest na Rysunku 4. Web Service PHP5 serwer klient w MS.NET Mamy już działającą usługę sieciową. Napisaliśmy do niej klienta w PHP5. To nic nadzwyczajnego. Połączmy się do niej z innej platformy technologicznej. Dodanie usługi sieciowej jest dziecinnie proste. Będąc w oknie Solution, wystarczy prawym przyciskiem myszki kliknąć na Project, następnie wybrać Add public long getlong() { return Int64.MaxValue; [WebMethod] public int getint() { return Int32.MaxValue; [WebMethod] public bool[] getbooleanarray() { return new bool[] { true, false, false, true ; [WebMethod] public object[] makeitarray(int i, double d, string s, bool b) { Object[] o = new Object[] {i, d, s, b; return o; Rysunek 7. Usługa sieciowa napisana w PHP5 wywołana z poziomu Java Mobile www.phpsolmag.org 57

Narzędzia SOAP w PHP5 Listing 10. Klient PHP5 łączący się do usługi MS.NET require_once ('Strings.inc.php' $client = new SoapClient( 'http://localhost/webservicetest/webservice.asmx?wsdl', array('trace' => 1) $functions = $client-> getfunctions( echo '<pre>'; var_dump($functions $string = $client->getstring( var_dump(strings::convertfromutftoiso($string->getstringresult) $double = $client->getdouble( var_dump($double->getdoubleresult $int = $client->getint( var_dump($int->getintresult // UWAGA!! // MS Int64.MAX_VALUE // jest w PHP jako float! $long = $client->getlong( var_dump($long->getlongresult $booleanarray = $client->getbooleanarray( var_dump($booleanarray->getbooleanarrayresult->boolean // nie zadziała! //$response = $client->makeitarray(123, -123.0, "string", true class MakeItArray { public $i; public $d; public $s; public $b; public function construct($i, $d, $s, $b) { $this->i = $i; $this->d = $d; $this->s = $s; $this->b = $b; $response = $client->makeitarray(new MakeItArray(123, "-123.0", "string", true) var_dump($response->makeitarrayresult->anytype echo '</pre>'; Web Reference (Zobacz Rysunek 6) i podać adres definicji WSDL. U nas będzie to: http://t:81/soap/article/prosteoperacj ews.php?wsdl. Wszystkie typy zdalne i obiekty służące do połączeń są domyślnie wrzucane do przestrzeni nazw takiej, jak ma nasz projekt z rozszerzeniem nazwy hosta z jakiego pochodzą. Czyli, wywołanie zdalne będziemy zapisywali tak (mój testowy host to t od słówka test): t.prosteoperacjeservice proxy = t.prosteoperacjeservice( int id = 1212; string artykul = proxy.pobierzartykul(id Web Service PHP5 serwer klient w Java SE Posłużę się tutaj środowiskiem NetBeans. Stwórzmy nowy projekt. Po utworzeniu kliknijmy na nim prawym przyciskiem o okienku Navigator i wybierzmy opcję: New -> Web Service Client. Następnie podajemy adres WSDL usługi i klikamy OK. Po kilku sekundach będziemy mieli utworzone wszystkie niezbędne pliki do połączeń z usługą. Możemy też skorzystać z narzędzia do testowania zdalnych wywołań, za pomcą kliknięć w NB możemy wywołać dowolną metodę z dowolnymi parametrami. Jeszcze tylko jedna mała uwaga: kody źródłowe klas NB generuje dość niestandardowo do katalogu bin. Należy je ręcznie skopiować do katalogu src! Następnie, w obsłudze konkretnej akcji piszemy: ProsteOperacjeService po = new ProsteOperacjeService_Impl( this.wywolanausluga.settext((string)this. try { listauslug.getselecteditem() this.wynikuslugi.settext (po.getprosteoperacje(). pobierznajnowszyartykul() catch (RemoteException ex) { ex.printstacktrace( catch (ServiceException ex) { ex.printstacktrace( Wynik działania graficznego klienta Java SE możemy zaobserwować na Rysunku 7. Rysunek 8. Klient PHP5 łączący się z usługą MS.NET, rezultat Web Service PHP5 serwer klient w Java Mobile Aby połączyć się z usługą sieciową w J2ME mamy do wyboru: natywny JSR 175 (dość ograniczony, w dodatku ofe- 58 www.phpsolmag.org

Narzędzia SOAP w PHP5 Listing 11. Przykładowa usługa sieciowa napisana w Java'ie public class WebServiceTest { public String getstring() { return "Jestem łańcuchem ąśź"; public double getdouble() { return -123123.23123d; public long getlong() { return Long.MAX_VALUE; public int getint() { return Integer.MAX_VALUE; public boolean[] getbooleanarray() { return new boolean[] { true, false, false, true ; public Object[] makeitarray(int i, double d, String s, boolean b) { Object[] o = new Object[] {new Integer(i), new Double(d), s, new Boolean(b) ; return o; Listing 12. Klient PHP5 łączący się do usługi AXIS (Java) require_once ('Strings.inc.php' $client = new SoapClient( 'http://localhost:8080/axis/webservicetest.jws?wsdl', array('trace' => 1) $functions = $client-> getfunctions( echo '<pre>'; var_dump($functions // UWAGA! // AXIS zwraca odpowiedź w formacie: // Jestem Ĺ aĺ cuchem // Ä Ĺ Ĺş // jest ona błędna - polskie znaki nie są kodowane w UTF-8 // tylko zamieniane na encje, stąd problem ponieważ PHP // próbuje najpierw rozkodować UTF-8 var_dump($client->getstring() var_dump($client->getdouble() var_dump($client->getint() // UWAGA!! // Java Long.MAX_VALUE // jest w PHP jako float! var_dump($client->getlong() var_dump($client->getbooleanarray() // działa bez problemu! // nie trzeba niczego pakować w obiekty var_dump($client->makeitarray(123, -123.0, "string", true) echo '</pre>'; rujący tylko tryby DOCUMENT/literal) lub skorzystać z darmowych bibliotek typu ksoap czy WingFoot. Ja użyję ksoap. Aby połączyć się z usługą należy utworzyć obiekt HttpTransport podać adres URL do usługi (nie do WSDL, ksoap sam pobierze WSDL dodając?wsdl do adresu). Tworzymy żądanie jako SoapObject drugim parametrem jest nazwa zdalnej metody. Jeśli metoda ma parametry dodajemy je jako property. Służy do tego metoda addproperty pierwszym argumentem jest nazwa parametru, drugim jego wartość (jako obiekt, dlatego użyłem Integer, a nie int). Samo łączenie się do usługi musi być wykonane w odrębnym wątku, inaczej emulator generuje ostrzeżenie o możliwym dead-locku podczas prawdziwych połączeń z komórki, tworzę więc Thread t = new Thread() {. Przedstawia to Listing 8. PHP5 jako klient Jak zauważyliśmy Java, Java mobile czy.net komunikuje się bez problemu z naszym serwerem SOAP. Java pracuje na Unicode,.NET zazwyczaj na WinCp1250, a jak zauważyliśmy, wszystkie znaki są poprawnie przedstawiane, nie ma także problemów z przesyłaniem różnych typów danych. Ale to tylko i wyłącznie dlatego, że to Java czy.net biorą na siebie wszystkie rzeczy związane z przekształceniem odpowiedzi na swoją wewnętrzną reprezentację. Co będzie, jeśli to my z poziomu PHP5 będziemy wołali metody z.net czy z Jakarta AXIS? Web Service PHP5 klient serwer MS. NET Stwórzmy testową usługę sieciową napisaną w C#. Jej kod przedstawiony jest na Listingu 9. Dla skrócenia listingu, usunąłem automatycznie wygenerowane metody typu: InitializeComponent, oraz usunąłem dyrektywy using. Widzimy, że mamy zdefiniowane operacje, które mają nam zwrócić różne typy danych, począwszy od int, double, string po tablice typów prostych a skończywszy na tablicach obiektów. (w.net int, double, string czy bool są równoznaczne z obiektami). 60 www.phpsolmag.org

SOAP w PHP5 Narzędzia Bardzo pożyteczną funkcją, z jakiej skorzystamy, jest getfunctions, która jest wywoływana na obiekcie SoapClient, a która zwraca nam sygnatury zdalnych metod. Z nich dowiemy się np. że MS.NET opakowuje wszystkie wiadomości zarówno input jak i output. I tak np. $client->getstring() zwraca nam obiekt, w którym znajduje się pole getstringresult. Aby wywołać metody z parametrami, musimy utworzyć obiekt, którego nazwy pól są takie same jak nazwy parametrów usługi MS.NET. Listing 7 wyjaśni wszelkie wątpliwości, a Rysunek 8 potwierdzi poprawne działanie naszego kodu. Klient Web Service PHP5 Serwer Jakarta AXIS (Java) Stwórzmy teraz analogiczny kod usługi sieciowej AXIS. Jest on pokazany na Listingu 11. Usługi sieciowe JWS to normalne kody źródłowe Java'y z rozszerzeniem nie java, a właśnie jws, które kopiujemy (tak, tylko kopiujemy!) bezpośrednio do $CATALINA_HOME/web apps/axis/. Znów skorzystamy z funkcji get- Functions, która zwróci nam sygnatury zdalnych metod. Z nich dowiemy się np. że Jakarta AXIS w przeciwieństwie do MS.NET nie opakowuje wszystkich wiadomości. Możemy z nich korzystać wprost tak, jakby były lokalnie wywoływanymi funkcjami. Ale zauważymy, że coś jest nie tak. Polskie kodowanie! Tak, jeśli podejrzymy koperty SOAP wymieniane z usługą AXIS okaże się, że polskie znaki nie są kodowane w UTF-8, tylko są zamieniane na kody znaków typu ї. Jest to niewątpliwie błąd AXISa, można oczywiście z tego wybrnąć na co najmniej kilka sposobów, ale należy o tym pamiętać pisząc własny system AXIS nie spełnia standardu! Listing 12 pokazuje jak wywoływać usługi sieciowe Java'y w PHP5, a Rysunek 9 potwierdzi poprawne działanie naszego kodu. Typy złożone i mapowania Nie jest sztuką przesłać obiekt typu string czy int, sztuką jest przesłanie obiektu PHP do np. MS.NET czy do Java'y. Załóżmy, że mamy usługę sieciową z metodą, która zwraca klasę Autor, z polami nazwisko typu string oraz polem ksiazki, które jest tablicą obiektów Ksiazka. W WSDL będziemy musieli zde- Listing 13. Definicja WSDL typów złożonych: autor, ksiazka oraz tablica książek <wsdl:types> <schema targetnamespace="http://t:81/soap/article/complextypews.php" xmlns="http://www.w3.org/2001/xmlschema"> <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/> <xsd:complextype name="autor"> <xsd:all> <xsd:element name="nazwisko" type="xsd:string"/> <xsd:element name="ksiazki" type="impl:arrayof_impl_ksiazka"/> </xsd:all> </xsd:complextype> <xsd:complextype name="ksiazka"> <xsd:all> <xsd:element name="tytul" type="xsd:string"/> <xsd:element name="isbn" type="xsd:int"/> </xsd:all> </xsd:complextype> <complextype name="arrayof_impl_ksiazka"> <complexcontent> <restriction base="soapenc:array"> <attribute ref="soapenc:arraytype" wsdl:arraytype="impl: ksiazka[]"/> </restriction> </complexcontent> </complextype> </schema> </wsdl:types> Listing 14. Definicja WSDL typów złożonych: autor, ksiazka oraz tablica książek if (0 == strcmp($_server['query_string'], '?wsdl')) { header('content-type: text/xml' readfile('complextype.wsdl' die( class Autor { private $ksiazki = array( function construct($nazwisko) { $this->nazwisko = $nazwisko; function dodajksiazke(ksiazka $ksiazka) { $this->ksiazki[] = $ksiazka; class Ksiazka { private $tytul; private $isbn; function construct($tytul, $isbn) { $this->tytul = $tytul; $this->isbn = $isbn; // klasa usługi sieciowej class ComplexType { function PobierzAutora($id) { // załóżmy, że szukamy w bazie autora o id = $id i znaleźliśmy $autor = new Autor('Budnik' $autor->dodajksiazke(new Ksiazka('PHP vs.net vs Java vs J2ME', 1212) $autor->dodajksiazke(new Ksiazka('PHP LIZ DB', 1313) return $autor; // mapowanie klas! TypZłożonyWSDL => NazwaKlasyPHP $classmap = array('autor' => 'Autor', 'ksiazka' => 'Ksiazka' $server = new SoapServer("ComplexType.wsdl" $server->setclass("complextype" $server->handle( www.phpsolmag.org 61

Narzędzia SOAP w PHP5 Listing 15. Kod klasy C# Ksiazka, z publicznymi property i metodą konwertującą obiekty class Ksiazka { private xh.ksiazka ksiazka; public string Tytul { get { return ksiazka.tytul; public int ISBN { get { return ksiazka.isbn; public Ksiazka(xh.ksiazka ksiazka) { this.ksiazka = ksiazka; public static Ksiazka[] Convert(xh.ksiazka[] ksiazki) { Ksiazka[] dotnetksiazki = new Ksiazka[ksiazki.Length]; for (int i = 0; i < ksiazki.length; i++) { dotnetksiazki[i] = new Ksiazka(ksiazki[i] return dotnetksiazki; Listing 16. Wgranie usługi na serwer z obsługą SSL i zmienienie w WSDL elementu <address> atrybutu location <wsdl:service name="prosteoperacjeservice"> <wsdl:port binding="impl:prosteoperacjesoapbinding" name="prosteoperacje"> <wsdlsoap:address location="https://ers:4433/soap/article/ ProsteOperacjeWSSecExt.php"/> </wsdl:port> </wsdl:service> Listing 17. Szkielet nowej klasy serwera class SecExtSoapServer extends SoapServer { public function construct($wsdl) { parent:: construct($wsdl public function handle($rawdata) { // sprawdź dane użytkownika $this->checkcredentials($dom // zapisz zmodyfikowane żądanie i prześlij do klasy rodzica parent::handle($dom->savexml() private function CheckCredentials(&$dom) { // tu napiszemy własną obsługę WS-SE Listing 18. Częściowy kod serwera $nodes = $dom->getelementsbytagnamens ('http://docs.oasis-open.org/wss/ 2004/01/oasis-200401-wss- wssecurity-secext-1.0.xsd', 'UsernameToken' if ($nodes->length == 0) { $valid = false; else { // użytkownik jest tu: $nodes->item(0)->firstchild->textcontent; // hasło tu: $nodes->item(0)->firstchild->nextsibling->textcontent; $valid = true; finiować trzy typy złożone: autor, książka oraz tablicę książek. WSDL przedstawiony jest na Listingu 13. Klasa PHP implementująca np. typy autor czy ksiazka, wcale nie musi mieć publicznych pól. Mogą one być ukryte we wnętrzu jako pola private. Wystarczy, że jest pewien mechanizm za pomocą, którego możemy te pola ustawić. W naszym prostym przykładzie postanowiłem przypisywać pola w konstruktorach. Spójrzmy na implementację i kod usługi sieciowej zamieszczonej na Listingu 14. Połączmy się z nią w MS.NET. Platforma Microsoft wykonuje mapowanie praktycznie za nas, ja napisałem dodatkowo klasę Ksiazka ze statyczną metodą Convert. Aby móc dodać do Data- Source tablice klas, klasy muszą mieć stworzone publiczne property, które automatycznie będą mapowane na kolumny. Kod łączenia się z usługą wygląda jak poniżej, kod klasy Książka zamieszczony jest na Listingu 15, a wynik działania z kolei na Rysunku nr 10. t.complextypeservice proxy = new t.complextypeservice( t.autor autor = proxy.pobierzautora (Int32.Parse(this.idAutora.Text) nazwiskoautora.text = autor.nazwisko; ksiazkidatagrid.datasource = Ksiazka. Convert(autor.ksiazki Implementujemy SOAP Security Enhancements plus HTTPS serwer Implementacja SOAP w PHP5 zawiera jedynie podstawowe operacje. Jeśli chcemy skorzystać z jakiegoś rozszerzenia musimy sami je zaimplementować. Spróbujemy teraz udostępnić naszą usługę w sposób w pełni bezpieczny z uwierzytelnianiem oraz z przesyłaniem żądań i odpowiedzi po szyfrowanych łączach. Najprostsze jest oczywiście udostępnienie naszej usługi przez HTTPS, wymaga to jedynie wgrania naszej usługi na serwer z obsługą SSL i zmienienie w WSDL elementu <address> atrybutu location tak, jak przedstawia to Listing 16. Stwórzmy zatem na naszą nową klasę serwera, szkielet będzie wyglądał tak, jak na Listingu 17. Widzimy w nim, że rozszerzamy podstawową klasę SoapServer, nadpisujemy tylko metodę handle, w której z $rawdata wczy- 62 www.phpsolmag.org

SOAP w PHP5 Narzędzia Listing 19. Kod serwera SOAP wspierającego WS Security Enhancements if (0 == strcmp($_server['query_string'], 'wsdl')) { header('content-type: text/xml' readfile('prosteoperacjesecext.wsdl' die( class SecExtSoapServer extends SoapServer { public function construct($wsdl) { parent:: construct($wsdl else { // użytkownik jest tu: $nodes->item(0)->firstchild->textcontent; // hasło tu: $nodes->item(0)->firstchild->nextsibling ->textcontent; // załóżmy, że tu połączymy się z bazą i to sprawdzimy $valid = true; public function handle($rawdata) { if (true == empty($rawdata)) { $this->fault('soap-env:server', 'Bad Request. Can\'t find HTTP_RAW_POST_DATA' $dom = new DOMDocument('1.0' try { // ładuję dokument danymi z POST $dom->loadxml($rawdata catch (DOMException $e) { $this->fault('soap-env:client', 'Bad Request.' // weryfikuję dane użytkowników $this->checkcredentials($dom // zapisz zmodyfikowane żądanie i // prześlij do klasy rodzica parent::handle($dom->savexml() private function CheckCredentials(&$dom) { $valid = true; $nodes = $dom->getelements ByTagNameNS( 'http://docs.oasis-open.org/wss/2004/01/ oasis-200401-wss-wssecurity-secext-1.0.xsd', 'UsernameToken' if ($nodes->length == 0) { $valid = false; // wyrzuć zdalny wyjątek jeśli jest błąd if (false == $valid) { $this->fault('soap-env:client', 'Invalid credentials' // teraz muszę usunąć nagłówki, które już przetworzyłem // domyślna implementacja PHP5 SOAP nie obsłuży nam // WS Security, a element Security ma podany atrybut // mustunderstand="1" wtedy przesłane do parent::handle // wyrzuci wyjątek, że nie zrozumiał $header = $dom->getelementsbytagnamens ( 'http://schemas.xmlsoap.org/soap/envelope/', 'Header')->item(0 // cały węzeł Security $security = $dom-> getelementsbytagnamens ( 'http://docs.oasis-open.org/ wss/2004/01/oasis-200401-wss-wssecurity-secext- 1.0.xsd', 'Security')->item(0 $header->removechild($security require_once ('Strings.inc.php' require_once ('ProsteOperacje.inc.php' $server = new SecExtSoapServer ('ProsteOperacjeSecExt.wsdl' $server->setclass('prosteoperacje' // trzeba jawnie podać: $HTTP_RAW_POST_DATA $server->handle($http_raw_post_datav R E K L A M A

Narzędzia SOAP w PHP5 Listing 20. Łączenie z usługą ers.prosteoperacjeservicewse proxy= new ers.prosteoperacjeservicewse( string pw = "I love Jessica Simpson"; proxy.requestsoapcontext.security.tokens.add ( new UsernameToken( "xh", pw, PasswordOption.SendPlainText ) try { textbox1.text = proxy.pobierznajnowszyartykul( catch (Exception ex) { System.Console.WriteLine( "Wyjątek: " + ex.gettype() + "\n" + ex.message + "\n" +ex.stacktrace Listing 21. Szkielet klienta przy zastosowaniu metody AddWSSEHeader class ExtendedSoapClient extends SoapClient { function construct($wsdl, $options = null) { parent:: construct($wsdl, $options function dorequest($request, $location, $action, $version) { $this->addwsseheader($dom // dorequest do rodzica return parent:: dorequest ( $request, $location, $action, $version public function SetUsername($username) { public function SetPassword($password) { private function AddWSSEHeader($domDocument) { // tutaj wygenerujemy nagłówek tamy dokument DOM XML (nie ma tego w powyższym szkielecie, nie ma także pokazanej weryfikacji, czy dane POST wprowadzone nie są puste, czy żądanie jest poprawnym dokumentem XML), przekażemy później przez referencję dokument do metody prywatnej CheckCredentials. W tej metodzie pobierzemy elementy <Username> oraz <Password> elementu <UsernameToken> wewnątrz <Security> w nagłówku koperty. Koperta ta została przedstawiona na Rysunku 1. Samo pobranie użytkownika i jego hasła jest sprawą banalną, wystarczy wyszukać element Username z przestrzeni nazw http: //docs.oasis-open.org/wss/2004/01/oasis- 200401-wss-wssecurity-secext-1.0.xsd, później, za pomocą API DOM XML, pobrać odpowiednie wartości. Praktyczna uwaga: dla hasła należy sprawdzić jeszcze atrybut Type, aby dowiedzieć się, czy hasło jet skrótem SHA1 czy PlainText. Sam kod jest prosty i przedstawia go Listing 18. Pełny kod serwera umieściłem na Listingu 19. Aby mieć pewność, że poprawnie zaimplementowaliśmy to rozszerzenie, stwórzmy projekt np. w MS.NET. W przypadku VS 2003 musimy mieć doinstalowany pakiet WS Security Enhancements 2.0 w wersji dla developerów. Dodajemy Web Reference jak poprzednio, podajemy adres do WSDL czyli: https://ers:4433/soap/article/prosteop eracjewssecext.php?wsdl, przejdźmy następnie do katalogu Web References i poprawmy MS, dopisując do każdego http:// kluczowe 's' czyli https://. Używam testowego certyfikatu serwera, który sam stworzyłem, muszę więc przekonać MS, że jest on wartościowy (inaczej nie będę mógł się połączyć z serwerem SOAP): najprościej jest otworzyć w IE stronę: https://ers:4433/soap/ article/prosteoperacjewssecext.php?wsdl lub dowolną z serwera ers:4433 i po prostu zainstalować certyfikat. Po zainstalowaniu i włączeniu WS SE 2.0, po wygenerowaniu referencji do usługi sieciowej będziemy mieli do dyspozycji dwa obiekty służące do wywoływania zdalnych usług. Pierwszy z nich o nazwie usługi z końcówką 'Service', jak dotychczas, oraz drugi obiekt z końcówką 'ServiceWse', który posiada dodatkowe, rozszerzające bezpieczeństwo, metody. To wszystko. Możemy już bez problemu połączyć się z naszą usługą pisząc to co prezentuje Listing 20. Efekt pokazany jest na Rysunku 11. Rysunek 11. Klient MS.NET łączący się po HTTPS do naszej usługi WS Security Enhancements Implementujemy SOAP Security Enhancements plus HTTPS klient Napisanie klienta w PHP5 łączącego się z naszą usługą jest proste. Musimy udostępnić publiczne metody, służące do ustawienia użytkownika i jego hasła, następnie musimy nadpisać metodę dorequest i napisać własną prywatną metodę AddWSSEHeader, która będzie generowała odpowiednie nagłówki WS SE. Szkielet klienta wygląda tak jak na Listingu 21. Pełny kod klienta umie- 64 www.phpsolmag.org

SOAP w PHP5 Narzędzia Listing 22. Kod klienta SOAP wspierającego WS Security Enhancements i łączącego się z usługą po HTTPS class ExtendedSoapClient extends SoapClient { private $Username; private $Password; function construct($wsdl, $options = null) { parent:: construct($wsdl, $options function dorequest ($request, $location, $action, $version) { $dom = new DOMDocument('1.0' try { $dom->loadxml($request catch (DOMException $e) { die('dokument nie jest prawidłowy: '. $e->code // dodaję nagłowek WSSE Security Header $this->addwsseheader($dom // musimy recznie dodac https, bo inaczej nici... // PHP tez sobie do konca nie radzi z HTTPS $location = 'https://vega.eti.pg.gda.pl/~xh/soap_ssl/ ProsteOperacjeWSSecExt.php'; // dorequest do klasy nadrzędnej SoapClient return parent:: dorequest($dom->savexml(), $location, $action, $version public function SetUsername ($username) { $this->username = $username; public function SetPassword ($password) { $this->password = $password; private function AddWSSEHeader ($domdocument) { $soapenvelope = $domdocument-> documentelement; // pobieram nagłówek $nodes = $domdocument->getelementsbytagnamens ('http://schemas.xmlsoap.org/ soap/envelope/', 'Header' // jeśli go nie ma muszę go stworzyć if ($nodes->length == 0) { $header = $domdocument-> createelementns ('http://schemas.xmlsoap.org/ soap/envelope/', 'Header' $body = $domdocument-> getelementsbytagnamens ('http://schemas.xmlsoap.org/soap/ envelope/', 'Body' $body = $body->item(0 $header = $soapenvelope-> insertbefore($header, $body else { $header = $nodes->item(0 // tworzę właściwy element Security $security = $domdocument->createelementns ('http://docs.oasis-open.org/wss/2004/01/ oasis-200401-wss-wssecuritysecext-1.0.xsd', 'wsse:security' // w nim element UsernameToken $usernametoken = $domdocument->createelementns('http:// docs.oasis-open.org/wss/2004/01/oasis-200401- wss-wssecurity-secext-1.0.xsd', 'UsernameToken' // w nim Username $username = $domdocument->createelementns ('http://docs.oasis-open.org/wss/2004/01/ oasis-200401-wss-wssecurity-secext-1.0.xsd', 'Username' $usernametext = $domdocument->createtextnode ($this->username // załóżmy, że Password zawsze będzie PlainText $password = $domdocument->createelementns ('http://docs.oasis-open.org/wss/2004/01/ oasis-200401-wss-wssecurity-secext-1.0.xsd', 'Password' $passwordtext = $domdocument-> createtextnode($this->password $type = $domdocument->createattribute('type' $type->value = 'http://docs.oasis-open.org/wss/2004/01/ oasis-200401-wss-username-token-profile- 1.0#PasswordText'; // podpinam wszystkie elementy do dokumentu żądania $username->appendchild($usernametext $password->appendchild($type $password->appendchild($passwordtext $usernametoken->appendchild($username $usernametoken->appendchild($password $security->appendchild($usernametoken $header->appendchild($security ściłem na Listingu 22. Aby jednak wywołać taką zdalną zabezpieczoną usługę, musimy stworzyć odpowiedni certyfikat. Konia z rzędem temu, kto wyczyta w dokumentacji PHP5, jak to zrobić. Certyfikat, jaki jest potrzebny, aby móc łączyć się po HTTPS do usług sieciowych to połączenie klucza prywatnego i certyfikatu w jednym! Wystarczy połączyć oba te pliki w jeden i zapisać jako np. cert.pem Wywołanie takiej metody jest już banalne, sprowadza się jedynie to 3 logicznych linii: require_once ( 'ExtendedSoapClient.inc.php' $client = new ExtendedSoapClient( ) 'https://ers:4433/soap/article/ ProsteOperacjeWSSecExt.php?wsdl', array('trace' => 1, 'exceptions' => 1, 'local_cert' => 'cert.pem' $artykuly = $client-> Pobierz10Najnowszych( Podsumowanie Nauczyliśmy się dziś, jak współpracować z innymi platformami technologicznymi. Teraz śmiało możemy współtworzyć systemy rozproszone na bazie Web Services komunikujących się z takimi gigantami jak MS.NET, J2EE, Java Mobile. Więcej, możemy korzystać z różnych rozszerzeń standardu WS Security Enhancements, które możemy z łatwością w kilka chwil zaimplementować. O autorze Łukasz Budnik jest studentem V roku Informatyki na Politechnice Gdańskiej, uczestniczy w projektach nad rozwijaniem systemów zbierania i analizy danych w postaci XML, jest rówież administratorem serwisu www.komputery-internet.net. Kontakt: lukasz.budnik@komputery-internet.net www.phpsolmag.org 65