Felhasználók hitelesítésének megoldása SOAP alapú webszolgáltatásokban
September 3rd, 2008
A hitelesítés (felhasználói autentikáció) lényege, hogy minden
beérkező SOAP lekérésről megállapítjuk, hogy melyik felhasználóhoz tartozik.
Erre azért van szükség, mert nem szeretnénk, hogy bárki hozzáférjen mások adataihoz.
Lássunk három megvalósítási módot.
1. Message-level security
A legegyszerűbb megvalósítás, ha minden SOAP üzenet tartalmazni fogja a jelszót ill. felhasználónevet.
Például:
| Argument | Type | Meaning |
|---|---|---|
| apiKey | string | … |
| password | string | … |
| entriesPerPage | int | … |
Ennek a megoldásnak a legnagyobb előnye az egyszerű megvalósítás.
Másik előnye, hogy ez a megvalósítás állapotmentes, azaz követi a
"statelessness SOA principless"-t.
Building stateless services encourage loose coupling, reusability, and composability. [1]
Hátrányai közé sorolnám, hogy a felhasználót azonosító adatok titkosítás nélkül továbbítódnak minden egyes SOAP lekéréssel (amennyiben nem használunk SSL-t).
További hátránya, hogy az összes függvény argumentumlistája a fehasználónév/jelszó párossal kezdődik,
csak utánuk jönnek a hívásra specifikus argumentumok (call-specific fields). Ez sokaknak nem túl elegáns megoldásnak tűnhet.
2. SOAP fejléc
A felhasználót azonosító adatokat (felhasználónév,jelszó) megadhatjuk a SOAP üzenet Header elemében is.

Az előnye az, hogy a webszolgáltatást igénybe vévő klienseknek
nem kell a tagfüggvények meghívásakor megadni a felhasználót azonosító adatokat,
így csak a tagfüggvényekre specifikus adatokat kell megadni. A PHP SOAP kiterjesztése lehetővé
teszi azt is, hogy a kliensnek csak egyszer kelljen beállítani a SOAP fejléc tartalmát,
az összes további SOAP-lekérésbe automatikusan beilleszti a megadott fejlécet (példakód).
További előnye, hogy ez a megoldás is állapotmentes, azaz követi a
"statelessness SOA principless"-t.
Kisebb hátránya, hogy a klienseknek bonyolultabb a dolguk, mert nem biztos,
hogy mindenki tudja, hogyan kell SOAP fejlécet beállítani.
3. SOAP fejléc + munkamenet kezelése
Az előbb ismertetett megoldások mindegyike állapotmentes volt, és a kiszolgáló
minden egyes SOAP lekéréskor hitelesítést végzett.
Most csak egyszer végzünk hitelesítést (login metódus), majd minden további kérelem
egy egyedi munkamenet (session) azonosítót fog tartalmazni a SOAP Header elemben (SOAP fejléc). A
munkamenet azonosítót (sessionId) a webszolgáltatás Login metódusa adja vissza a kliensnek.
Ennek a módszernek két előnye is van.
-
Biztonságosabb megoldásnak mondható, hiszen
a titkos jelszót a kliens csak egyetlen SOAP lekérés alkalmából küldi el. Ezt akár
SSL kapcsolaton is küldheti, a többi lekérést pedig titkosítatlan HTTP-n
(persze csak akkor, ha a biztonság kérdése nem létfontosságú és fontosabb a teljesítmény). -
Léteznek olyan adatok (például a bevásárlókocsi tartalma), amelyeket meg kell
őrizni a kérelmek között. Használhatjuk például a PHP beépített munkamenet támogatását, azaz session változókat.
Hitelesítés + munkamenet kezelés megvalósítása PHP SOAP webszolgáltatásban
September 3rd, 2008
Ez az előző cikk folytatása konkrét példakódokkal.
Adatbázis
Tegyük fel, hogy az adatbázisban fogjuk tárolni a felhasználókat.
CREATE TABLE WsApiUsers (
ApiKey Char(32) NOT NULL UNIQUE,
SecretKey VARCHAR(40) NOT NULL UNIQUE,
PRIMARY KEY (ApiKey)
);
Az ApiKey egyedi azonosító a felhasználók azonosítására szolgál.
A SecretKey egy titkosító kulcs, ami nem összetévesztendő
a jelszóval. Ezzel a titkos kulccsal kell a felhasználónak titkosítania egy
sztringet (a sessionId-t), amit majd a kiszolgálótól kap.
Szükségünk van még egy adatbázis táblára, amelyben az
éppen aktív munkamenetek listáját fogjuk tárolni.
Minden munkamenetet az egyedi SessionID azonosít, és ehhez még különböző információk társulnak.
| SessionID | A munkamenet egyedi azonosítója. |
| IP | A kliens IP cime. Jól jöhet ez az információ, ha a későbbiekben úgy döntünk, hogy feljavitjuk az autentikációt. Például az autentikáció a jelszó és a felhasználói néven kívül ellenőrizné még a kliens IP címét is. Csak egy bizonyos IP címről, vagy IP cím subnetről érkező SOAP üzeneteket engedne tovább. Így valamelyest erősítenénk a webszolgáltatás biztonságát. |
| UserID | Ez egy idegen kulcs, és a WsApiUsers adatbázis táblára mutat. Ha értéke NULL, a kliens még nem jelentkezett be (nincsen autentikálva), kiléte ismeretlen. |
CREATE TABLE WsActiveSessions (
SessionID Char(40) NOT NULL UNIQUE,
IP Char(32) NOT NULL,
UserID SMALLINT UNSIGNED NULL,
Primary Key (SessionID),
FOREIGN KEY (UserID) REFERENCES WsApiUsers (ID)
)
ENGINE=HEAP
MAX_ROWS = 250;
A kliens
A kliensnek először egy munkamenet azonosítót kell kérnie a getSessionId tagfüggvény meghívásával.
Második lépésben összefűzzük a titkos kulcsot a kiszolgálótól kapott sessionId-val, majd a kapott
karakterláncból egy 128 bites kivonatot képezünk MD5 titkosítással. Ebben az a jó, hogy a titkos kulcsunkat nem
küldjük el a hálózaton keresztül.

Figyeljük meg az XML üzeneteket. Vegyük észre, hogy a login metódus meghívása már
tartalmazza a SOAP Header elemben a munkamenet azonosítót. A getSessionId metódus ugyanis
az egyetlen metódus, amely meghívható üres SOAP Header elemmel, a többi metódus
megköveteli a sessionId jelenlétét a SOAP fejlécben.
A kiszolgáló
Kezdjük a WSDL megírásával.
A munkamenet azonosítót így deklaráltuk a WSDL fájlban:
Arra van szükségünk, hogy hozzáférjünk a SOAP üzenet fejlécében lévő sessionId elem tartalmához.
Erre konkrét példát egy előző blogbejegyzésben találunk:
"PHP SOAP kiterjesztés - SOAP Header használata".
Továbbá azt is tudnunk kell, hogy a kliens melyik metódust hívta meg.
Ezt is az XML üzenetből tudjuk kiparszolni a köv. kóddal (a lényeg az XPath kifejezés).
Ha a kliens a getSessionId tagfüggvényt hívta meg, akkor nem ellenőrizzük a sessionId jelenlétét a SOAP fejlécben.
Ellenkező esetben SOAP fault-ot dobunk, ha a sessionId hiányzik a SOAP fejlécből.
Amikor megkaptuk a sessionId-t a SOAP üzenetben, leellenőrizzük, hogy létezik-e ilyen aktív munkamenet a
WsActiveSessions adatbázis táblában. Ha igen, akkor felújítjuk a PHP sessiont a
session_id() PHP funkcióval.
Ettől a pillanattól kezdve elérhetőek a klienshez tartozó session változók is.
PHP SOAP kiterjesztés - SOAP Header használata
September 3rd, 2008
Minden SOAP üzenet tartalmaz egy kiegészítő SOAP fejlécet (Header elem).
Az opcionális fejléc elem, a Header kiegészítő adatok megadására szolgál.
Ugyanabból a célból létezik, mint a HTTP Header. Leggyakrabban a Header elembe
a munkamenet (session) azonosítót, vagy a kliens hitelesítéséhez szükséges adatokat teszik.
Kliens
Lássuk, hogyan kell a PHP5-ben megjelent SOAP kiterjesztéssel beállítani egy SOAP fejlécet.
Tegyük fel, hogy a munkamenet azonosítot kell elküldenünk minden egyes SOAP üzenetben.
A Header elem bármilyen elemeket tartalmazhat, csakis tőlünk függ a tartalma
(na meg persze a kiszolgáló oldalon a WSDL fájlban deklaráltaktól).
A példa kedvéért a munkamenet azonosítót egy sessionId nevű elemben kell elhelyeznünk,
ami egy további ApiUserAuthHeader szülő elemben kell, hogy legyen. Ezek az elemek pedig a
urn:ExampleAPI névtérben kellenek, hogy legyenek.
Lássunk egy kész SOAP üzenetet, amit a kliens küld a kiszolgálónak.
Íme a PHP kód:
A SoapHeader osztály
szolgál SOAP fejléc létrehozására. Létre kellett hoznunk egy ApiUserAuthHeader osztályt, aminek egy
sessionId tulajdonságot adunk.
Annak ellenére, hogy a SoapHeader konstruktorában, az első paraméterrel beállítottuk az urn:ExampleAPI, a SOAP
kiterjesztés valamilyen okból mégsem ad a sessionId elemnek prefixet,
ezért volt szükség az ApiUserAuthHeader osztály konstruktorában a sessionId-t SoapVar objektummal létrehozni
(egyébként elég lett volna egy sima publikus sessionId nevű változó is).
Erről itt olvashatunk többet: http://bugs.php.net/bug.php?id=40318&edit=1.
A login metódusnak a paramétereket így is átadhattuk volna, ez is helyes megoldás:
Ha viszont így hívtuk volna meg a login metódust, akkor a SOAP kiterjesztés helytelenül kódolta volna a paramétereket a SOAP törzsben.
$ret = $client->__call('login',array('WorkaholicGroupApiKey','0c9ed93ec403851a2f7682aceb98a5a9'));
Kiszolgáló
Kiszolgáló oldalon a következőkre van szükségünk:
Elkapni a beérkezett SOAP XML üzenetet
$request = file_get_contents('php://input');
Kiparszolni belőle a sessionId elem tartalmát
