Feb 21
  • english
  • german

Für TYPO3 gibt es ja bereits allerlei Extensions die das Login irgendwie modifizieren. Mal über LDAP, mal per Zertifikat kann man sich dann einloggen. Was es allerdings noch nicht gibt, ist die Möglichkeit sich per Webservice anzumelden. Ich gehe mal davon aus es gibt schon einen, um den Webservice Server möchte ich mich hier also nicht kümmern. Fangen wir also im Kickstarter an: (diese Extension muss man sich aus dem TER herunterladen, installieren und dann im Dropdown links oben im Extension Manager auf „create new Extension“ klicken). Hier gibt man die allgemeinen Daten zur Extension ein und legt danach einen neuen Service an. Der Servicetyp muss „auth“ sein und beim Service-Subtyp kommt es ganz darauf an, was der Service alles können soll. In unserem Fall soll der Service einfach nur die ganzen Nutzerdaten abrufen damit wir die nicht im TYPO3 verwalten müssen. Außerdem soll man sich natürlich anmelden können. Für diese Funktionalität muss beim Subtyp folgendes eingetragen werden: „getUserFE,authUserFE,getGroupsFE“. Bei Priorität und Qualität muss ein Wert eingetragen werden, der höher ist als der des normalen Loginservice (50). Ich habe also bei beiden Feldern eine 60 eingetragen. Das war es eigentlich auch schon im Kickstarter. Jetzt noch die Extension abspeichern und dann kanns losgehen. (weitere Infos zum auth-Service)

Unsere Serviceklasse befindet sich unter typo3conf/ext/EXTENSIONNAME/sv1/class.tx_XXX_sv1.php. Dort müssen wir die erste Änderungen vornehmen: Unsere Klasse muss nämlich von „tx_sv_authbase“ erben, und nicht von „t3lib_svbase“. Nun können wir uns an die Implementierung machen.

Die Methode init braucht gar nicht viel zu tun:

function init() {
	$this->writeDevLog = true;
	return parent::init();
}

Es müssen allerdings noch ein paar weitere Methoden geschrieben werden. Als erstes wird beim Login die Methode getGroups aufgerufen um zu sehen was es alles für Nutzergruppen gibt. Da könnte man nun auch einen Webserviceaufruf hineinpacken und sich die Gruppen vom Server geben lassen. Da ich das Beispiel hier einfach halten möchte, geben wir einfach ein Dummy-Array zurück:

function getGroups() {
	$group = array('uid' => array(1),
		'title' => array('nogroup'),
		'pid' => array(0)
	);
	return $group;
}

Man erkennt schon die Struktur. Es ist ein Array mit 3 Unterarrays in denen dann die Gruppeninfos stehen. Als nächstes wird die Mehtode getUser aufgerufen um zu prüfen ob der Nutzer überhaupt existiert. Eigentlich ist der Service so gedacht, dass erst der ganze Nutzer geholt und dann validiert wird. Das ist bei einem Webservice allerdings relativ unsicher, da wir ja gleich alle Nutzerdaten übertragen würden, bevor er überhaupt eingeloggt ist. Also werden wir nur Nutzerdaten zurückbekommen, wenn das Passwort auch gestimmt hat. Dummerweise prüft TYPO3 nach unserer Anmeldung den Nutzer nochmal in der eigenen fe_users-Tabelle. Deshalb müssen wir (mindestens) den Nutzernamen und die Id auch lokal speichern:

function getUser() {
	$user = false;
	if ($this->login['status']=='login' AND $this->login['uident'])	{
		$user = $this->getUserFromWebservice($this->login['uname'], $this->login['uident']);
		if(!is_array($user)) {
			// Failed login attempt (no username found)
		} else {
			//write userdata to internal db
			//DON'T FORGET TO UTF8_DECODE ALL STRING PROPERTIES HERE! (I skip this part in the example)
			$resCount = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', 'fe_users', 'uid = '.$user['uid']);
			//leave out password and other critical data!
			$userForDb = array(
				'uid'	=> $user['uid'],
				'username' 	=> $user['username']
			);
			if($GLOBALS['TYPO3_DB']->sql_num_rows($resCount) == 1) {
				//update
				$res = $GLOBALS['TYPO3_DB']->exec_UPDATEquery('fe_users', 'uid = '.$userForDb['uid'], userForDb);
			} else {
				//insert
				$res = $GLOBALS['TYPO3_DB']->exec_INSERTquery('fe_users', $userForDb);
			}
			//if internal db operation failed, login also does
			if(!$res) {
				return false;
			}
		}
	}
	return $user;
}

Der eigentlich Aufruf zum Webservice wurde hier nochmal gekapselt denn SOAP versteht nur UTF8-Strings und wenn ein Umlaut in Nutzername oder Passwort einen Umlaut enthält, dann kommt ein SOAP-Fault zurück. Aus diesem Grund müssen wir alle Daten die wir senden noch UTF8-kodieren und auf der Serverseite wieder dekodieren:

function getUserFromWebservice($username, $password) {
	try {
		//connect
		$soapclient = new SoapClient('ENTER_YOUR_WSDL_PATH_HERE', array('trace' => 1));
		//construct data
		$data = new stdClass();
		$data->username = utf8_encode($username);
		$data->password = utf8_encode($password);
		//login! (we pretend that the service method is called "loginUser")
		$response = $soapclient->oginUser($data);
	} catch(SoapFault $e) {
		trigger_error("SOAP Fault: (faultcode: {$e->faultcode}, faultstring: {$e->faultstring})", E_USER_ERROR);
	}
	//handle response
	return $this->handleWebserviceResponse($response);
}

Die Behandlung der Antwort wurde ebenfalls gekapselt:

function handleWebserviceResponse($responseObject) {
	if($responseObject->success) {
		return get_object_vars($responseObject->user);
	} else {
		//some error handling may be done here
		return false;
	}
}

Hier bedarf es nun noch einiger Erklärung: Die Variable $responseObject ist eine stdClass, genau wie der Input. Sie hat (so wurde unser Service geschrieben) 3 Eigenschaften: „success“ ist ein boolean, „errorCode“ ist ein Integer und „user“ ist wieder eine stdClass mit einigen Properties (uid, username, etc). Diese wird in ein Assoziatives Array umgewandelt und zurückgegeben. Dann wird (in der Methode getUser) der Nutzer noch in die lokale Tabelle fe_users übernommen. Das was diese Methode dann zurückliefert, wird schließlich noch an eine Methode „authUser“ übergeben die eigentlich das Login übernehmen soll. In unserem Fall ist das allerdings nicht mehr nötig, da der Nutzer ja schon authentifiziert wurde. Es genügt also einfach true zurückzugeben:

function authUser($user) {
	return true;
}

So, das war es schon. Um das Ganze zu testen, installiert man sich schnell die Extension „newloginbox“, bindet sie ein und probiert es aus. Wer noch Fragen hat, kann gerne Kommentare posten.(Download der Klasse)

15 Antworten zu “TYPO3-Login per Webservice”

  1. Grundi sagt:

    Da mir das WordPress beim speichern immer die schließenden Klammern (>) in Entities umwandelt (>), und ich keine Lust habe das jedesmal wieder per Hand zu ändern, gibt es den gesamten Code auch als Download am Ende des Posts. Also bitte verzeiht mir kleine Fehler die durch das Speichern des Posts reingekommen sind – Danke!

  2. Torben Dam sagt:

    Nach SOO lange suchen, und 3 Tage.. endlich finde ich hier eine Brauchbare Dokumentation wie’s mit getUser geht.

    DANKE !

    Es fehlt bestimmt Dokumentation über AUTH in Typo3.

  3. Grundi sagt:

    Hallo Torben.

    Freut mich, dass dir der Post geholfen hat. Zugegeben habe ich das aber auch nicht selbst durch probieren rausgefunden, sondern mir eine der LDAP-Extensions heruntergeladen um zu sehen wie es dort gemacht wird. Da habe ich dann alles nötige gefunden. Die Doku zu dem Thema ist, wie du schon sagtest, eher dürftig, aber das ist bei TYPO3 ja öfter mal so (ich möchte mich da selbst nicht ausschließen, meine Extension hat gar keine ^^).

  4. Thomas sagt:

    Hi,
    ich versuche ebenfalls eine Authentifizierung über ein propriäters System zu ermöglichen und stelle fest, dass der Service zu spät geladen wird!

    Die t3libdiv::makeInstanceService() sucht (mehrmals) über t3lib_extMgm::findService() einen geeigneten Service für die Authentifizierung, allerdings findet der t3lib_extMgm::addService() Aufruf meiner Extension zu spät statt, danach gibt es keine findService() Aufrufe mehr?!
    Meinen Service habe ich in der Extensionliste ganz nach vorn gestellt.

    Ich nutze 4.2.1 – irgendeine Idee dazu?

    Grüße,
    Thomas

  5. Thomas sagt:

    Kleines Update: Der Kickstarter legt den addService() Aufruf in die ext_tables.php, er muss allerdings in die ext_localconf.php! Dann klappts auch mit dem rechtzeitigen laden! Juhu!
    Grüße,
    t

  6. Sebastian sagt:

    Hi,
    super Artikel, hat mir sehr geholfen beim entwickeln eines Webservices.
    Mir hat es noch sehr geholfen die DevLog Messages der UserAuthend Klasse zu lesen.
    Anschalten kann man diese über den folgenden Eintrag in der ext_localconf.php:
    $GLOBALS[‚TYPO3_CONF_VARS‘][‚SC_OPTIONS‘][‚t3lib/class.t3lib_userauth.php‘][‚writeDevLog‘] = 1;
    danke und grüsse,
    sep

  7. Uwe sagt:

    Hallo,
    finde die Klasse sehr interessant. Ich möchte sie nutzen um über die Flash Remoting Extension (ryzy_flash_rm) ein Login zu ermöglichen.
    Frage: Wie erstellt man den Webservice der die Methode Login bereitstellt?
    Und Grundsätzlich frage ich mich, weshalb man nicht über das $GLOBALS-Objekt nicht eine methode ansprechen kann, z.B. loginUser($username,$password).

  8. Grundi sagt:

    Hi Uwe.

    Ich habe deine Struktur noch nicht ganz verstanden, wie arbeiten Webservice, Webseite und Flash genau zusammen? Wie man einen Webservice erstellt, kannst du mit „webservice php tutorial“ googeln oder in ein Fachbuch schauen, da steht das mit ziemlich ausführlichen Beispielen meist gut erklärt.

    Falls du sonst noch Hilfe brauchst, kannst du mich gerne kontaktieren.

    $GLOBALS ist nicht wirklich ein Objekt, sondern nur eine Sammlung von verschiedenen Dingen, die man sich sonst erst „holen“ müsste. Die Methode ist allerdings etwas antiquiert und sollte nicht wirklich verwendet werden. Zusätzlich muss eine neue Loginmethode auch erst am Core registriert werden, sonst kommt das TYPO3 ja ganz durcheinander und sonst könnte auch „jeder“ (irgendwo in seinem Quellcode) solche Aktionen durchführen, was der Sicherheit der Anwendung sicher nicht zuträglich sein würde 😉

    Die (nötige) Struktur mag am Anfang etwas komplex erscheinen, da man nicht alles ausimplementieren muss, relativiert sich das aber auch wieder…

    Ich hoffe, du kommst damit erstmal etwas weiter.
    Viele Grüße,
    Michael

  9. andy kreicy sagt:

    das ffunktioniert ja alles grossartig – ich schreibe den user in die db, bleibe aber nicht eingeloggt – sondern bekomme einen error.
    erst beim zweiten login bin ich eingeloggt als fe_user.

    was muss ich machen um auch schon beim ersten login als FE user angemeldet zu sein?

    lg aus wien
    andy

  10. andy kreicy sagt:

    habs schon!
    // CODE

    if (!$GLOBALS[‚TSFE‘]->fe_user) {
    if (!isset($GLOBALS[‚TYPO3_DB‘]) || !$GLOBALS[‚TYPO3_DB‘]->link) {
    tslib_eidtools::connectDB();
    }
    $GLOBALS[‚TSFE‘]->fe_user = tslib_eidtools::initFeUser();
    }
    $GLOBALS[‚TSFE‘]->fe_user->checkPid=0; //do not use a particular pid
    $info= $GLOBALS[‚TSFE‘]->fe_user->getAuthInfoArray();
    $user=$GLOBALS[‚TSFE‘]->fe_user->fetchUserRecord($info[‚db_user‘],$uname);
    $GLOBALS[‚TSFE‘]->fe_user->createUserSession($user);
    $GLOBALS[‚TSFE‘]->fe_user->user = $user;

    damit forced man den eingeloggten zustand!

  11. Peter sagt:

    Hi,
    habe mich genau an obigen Code gehalten, leider bekomme ich nur bei aktivierten error-messages:
    Fatal error: Class ’sdtClass‘ not found in …… on line 124.
    Wer kann mir bitte weiterhelfen (TYPO3 4.4.4)
    lg
    peter

  12. Grundi sagt:

    Hallo Peter.

    stdClass ist Teil von PHP. Wenn die bei dir nicht geht, dann ist wohl was mit dem PHP im argen. Welche PHP-Version nutzt du denn? Weitere Infos gibts auch unter http://php.net/manual/en/reserved.classes.php

    VG,
    Michael

  13. Peter sagt:

    Hi Michael,
    sorry hat eh funktioniert, muss natürlich „stdClass“ statt „sdtClass“ heißen – habs aus deinem Download der Klasse, da hast du einen kleinen Druckfehler 😉

    Authentifizierung müsste passen, jetzt hab ich nur noch das Problem, dass ich aus dem wsdl File mittels __soapCall Funktionen auslesen müßte und auf der Website als Content darstellen soll.
    Habe sonst nur eine Endpoint-URL und ein Beispiel-Request für das Service GetContractList, welches in xml geschrieben ist. Leider hab ich sowas noch nie gemacht und wäre über alle Tipps sehr dankbar.
    lg peter

  14. Thomas sagt:

    wie würde es denn funktionieren, wenn ich auf einer anderen Seite als Benutzer eingeloggt bin und über einen Link auf eine Typo3 Seite verweise, auf der ich nun als FE User angemeldet werden soll?

    Würde es funktionieren den Benutzernamen und das Passwort über die URL mit zu geben? (ob das sicher ist oder nicht wäre erstmal egal!)

    Ich verfüge hier nicht über einen Webservice… hätte hierzu jemand eine Idee?

  15. Grundi sagt:

    Hallo Thomas. Ja, auch das geht. Du muss die Parameter „user“, „pass“ und „logintype“ (Wert = „login“) an eine beliebiege TYPO3-Seite übergeben. Das Login übernimmt der Core, eine sinnvolle Ausgabe kannst du mit dem felogin Plugin (ist Standard) erzeugen.

    Sicher machst du es, indem du eine SSL-Verbindung verwendest.

Einen Kommentar schreiben