Ενσωμάτωση Single Sign-On σε εφαρμογές του Yii Framework

Καλησπέρα! Είμαι σίγουρος ότι όλοι έχετε ακουστά για την τεχνολογία Single Sign On (SSO). Είναι ωραία να την χρησιμοποιείς, αλλά τί γίνεται όταν πρέπει να την εφαρμόσεις? Θα σας δείξω ένα παράδειγμα ενσωμάτωσης SSO σε εφαρμογές του Yii (υπάρχουν πολλοί τρόποι να γίνει και εξαρτάται από την υλοποίηση του SSO-Server). [color="#FF0000"]Επειδή χρησιμοποιώ έναν έτοιμο SSO-Server, θα πρέπει πρώτα να κατανοήσετε τα βασικά της λειτουργίας του πριν πάτε να κατανοήσετε πως ενσωματώνεται σε εφαρμογές του Yii. [/color] Αν καταλάβετε τον SSO-Server, η ενσωμάτωση δεν θα σας δυσκολέψει. Το υπόσχομαι! :)

Πρόκεται να παρακάμψουμε το προεγκατεστημενο σύστημα ελέγχου πρόσβασης του Yii και να το αντικαταστήσουμε με το δικό μας που θα χρησιμοποιεί την τεχνολογία SSO. Η υλοποίηση του SSO-Server που θα χρησιμοποιήσουμε είναι βασισμένη σε αυτή του Arnold Daniels. Μπορείτε να την βρείτε εδώ. Λίγες παραπάνω λεπτομέρειες μπορείτε να βρείτε και εδώ(επιλέγοντας "Custom SSO"). Θα το τονίσω από τώρα για να μην μπερδευτείτε αργότερα: η μέθοδος αυτή αχρηστεύει (τουλάχιστον όπως την ξέραμε μέχρι τώρα σττην διαδικασία ελέγχου πρόσβασης) την χρήση των μεθόδων/κλάσεων accessControlFilter, accessRules, UserIdentity, CWebUser και ότι τμήμα κώδικα χρησιμοποιεί αυτές. Ας ξεκινήσουμε!

Βήμα 1: Φτιάχνουμε ένα φίλτρο το οποίο θα τοποθετήσουμε σε κάθε Controller στου οποίου τις ενέργειες θέλουμε να ελέγξουμε την πρόσβαση. Θα ονομάσουμε το φίλτρο AuthorizationFilter. H κλάση που το ορίζει θα βρίσκεται στο φάκελο “\protected\filters\” και θα είναι η εξής:


<?php 


class AuthorizationFilter extends CFilter {


	protected function preFilter($filterChain){

		if($this->isSSOLogged()) {

			$isLogged = true;

		} else {

			$isLogged = false;

		}

		$userAction = Yii::app()->Controller->getAction()->id;


		switch($userAction) {

			case 'index'  : return true; break;

			case 'create' : if ($isLogged) return true;

							Yii::app()->Controller->redirect(Yii::app()->user->loginUrl);

							break;

		}


		Yii::app()->Controller->redirect(Yii::app()->user->loginUrl);

		return false;

	}


	protected function postFilter($filterChain){

		return true;

	}


	public function isSSOLogged(){


		require_once(Yii::app()->basePath."/sso/sso.php");

		$sso = new SingleSignOn_Broker();

		if($sso->getInfo()) return true;

		return false;

	}


}

Η κλάση περιέχει μια συνάρτηση η οποία ελέγχει αν ο χρήστης είναι συνδεδεμένος στο SSO. Πριν την εκτέλεση κάθε ενέργειας ενός Controller, εκτελείται η μέθοδος preFilter(). Αυτή καταρχάς ελέγχει αν ο χρήστης είναι συνδεδεμένος και ποιά ενέργεια προσπαθεί να εκτελέσει. Κατόπιν, ανάλογα με το αν έχει δικαίωμα ή όχι μπορούμε να επιτρέψουμε την ενέργεια ή να φορτώσουμε κάποια άλλη σελίδα (π.χ ανακατεύθυνση στη σελίδα του login αν δεν είναι συνδεδεμένος ή ανακατεύθυνση σε μια προειδοποιητική σελίδα αν είναι συνδεδεμένος και δεν έχει δικαίωμα πρόσβασης).

Βήμα 2: Αφαιρούμε το φίτλρο AccessControl και προσθέτουμε το δικό μας φίτλρο σε όσους Controller θέλουμε να ελέγξουμε την πρόσβαση.


public function filters()

	{

		return array(

			array(

				'application.filters.AuthorizationFilter',

			),

		);

	}

Η μέθοδος accessRules() δεν χρειάζεται πια και μπορεί να διαγραφεί!

Βήμα 3: Τοποθετούμε το αρχείο sso.php του broker στη θέση “\protected\sso\”. Αυτό το κάνουμε σε κάθε εφαρμογή του Yii που θα χρησιμοποιεί το SSO. Μπορούμε να το βάλουμε όπου θέλουμε αλλά ο κώδικας του παραδείγματος που δίνω παρακάτω το τοποθετεί εκεί.

Βήμα 4: Στο ίδιο επίπεδο με τους φακέλους των εφαρμογών (Ε:\AppServ\www\ για το παράδειγμά μας) τοποθετούμε και έναν φάκελο με όνομα sso-server, ο οποίος θα έχει μέσα το αρχείο sso.php του SSO-SERVER.

Βήμα 5: Τροποποιούμε την μέθοδο login() της κλάσης LoginForm() ως εξής:


public function login()

	{

		require_once(Yii::app()->basePath."/sso/sso.php");

		$sso = new SingleSignOn_Broker();

		if($sso->getInfo()) return true;

			else return $sso->login($this->username,$this->password);

	}

H μέθοδος authenticate δεν μας χρειάζεται ποιά και μπορούμε να την διαγράψουμε.

Επίσης, οι κανόνες εγκυρότητας των στοιχείων της φόρμας δεν περιλαμβάνουν τον κανόνα του authenticate για το password πια:


public function rules()

	{

		return array(

			// username and password are required

			array('username, password', 'required'),

			// rememberMe needs to be a boolean

			array('rememberMe', 'boolean'),

		);

	}

Βήμα 6: Τροποποιούμε την μέθοδο actionLogout() του SiteController εως εξής:


public function actionLogout()

	{

		require_once(Yii::app()->basePath."/sso/sso.php");

		$sso = new SingleSignOn_Broker();

		$sso->logout();


		$this->redirect(Yii::app()->homeUrl);

	}

Βήμα 7: Πολύ πιθανόν να χρειαστούν τροποποιήσεις και τα μενού που εμφανίζονται σε κάθε view. Η τεχνική που ίσχυε μέχρι τώρα:


widget('zii.widgets.CMenu',array(

'items'=>array(

...

array('label'=>'Login', 'url'=>array('/site/login'), 'visible'=>Yii::app()->user->isGuest),

...

),

δεν ισχύει ποιά γιατί [color="#0000FF"]η εφαρμογή δεν χρησιμοποιεί πια sessions για τους συνδεδεμένους χρήστες[/color]. Ακόμα δεν είναι έγκυρη και η χρήση του checkAccess. Όπως καταλαβαίνεται, χρειαζόμαστε μια μέθοδο για να αποθηκεύουμε πληροφορία για κάποιον συνδεδεμένο χρήστη, την οποία θα χρησιμοποιούμε μέσα στον Controller ή σε κάποιο view. Ας δούμε μια λύση:

α. Τροποποίηση της κλάσης CWebUser. Προσθέτουμε ένα πεδίο για την κάθε πληροφορία που θέλουμε να αποθηκεύσουμε.

β. Τροποποιούμε τον SSO-SERVER ώστε να επιστρέφει την επιθυμητή πληροφορία

γ. Τροποποιούμε το sso.php του broker ώστε να προωθεί την σχετική πληροφορία μέσω της getInfo().

δ. Τροποποιούμε το AuthorizationFilter ώστε όταν ο χρήστης είναι συνδεδεμένος να διαβάζεται η σχετική πληροφορία που επιστρέφει ο sso-server και να αποθηκεύει την πληροφορία στα αντίστοιχα πεδία της κλάσης CWebUser:

Στο παράδειγμα που δίνω παρακάτω έχει υλοποιήσει κάτι πολύ πιο απλό για εκπαιδευτικούς σκοπούς. Προσθέτω ένα πεδίο για το όνομα χρήστη στην CWebUser.


public $username;

Όταν ο χρήστης είναι συνδεδεμένος το AuthorizationFilter δίνει μια τιμή (στατική εδώ) στο πεδίο αυτό.


if($this->isSSOLogged()) {

			Yii::app()->user->username = "Takis";

			$isLogged = true;

		} else {

			$isLogged = false;

		}

Στο μενού του αρχείου “\views\layouts\main.php” βάζω μια συνθήκη για τον αν θα πρέπει να δείχνει ή όχι την επιλογή “Logout”


array('label'=>'Logout ('.Yii::app()->user->name.')', 'url'=>array('/site/logout'),'visible'=>isset(Yii::app()->user->username))

Παρακάτω είναι τα αρχεία και τις βάσεις του παραδείγματος. Ίσως χρειαστεί να διορθώσετε κάποιο path. Εγώ τα είχα τοποθετημένα μέσα

στο “E:\AppServ\www\”.

[center][color="#2E8B57"]Τα αρχεία του παραδείγματος: [/color] 2483

Yii-with-SSO-example.zip

[color="#2E8B57"]Η βάση του Broker-1:[/color] 2484

broker1a-db.txt

[color="#2E8B57"]H βάση του Broker-2: [/color]2485

broker2a-db.txt

[color="#808080"]* οι βάσεις είναι αρχεία .sql που έχω αλλάξει την κατάληξη σε txt για να μπορώ να τα ανεβάσω![/color]