Ik ben bezig met een (single user) login systeem voor een (eigen) website, deels omdat ik een login systeem nodig heb en deels bij wijze van oefening met OOP. Laat ik beginnen met zeggen dat ik hier echt nog een beginneling mee ben en echt uren aan het twijfelen geweest ben hoe ik dingen aan moet pakken en uiteindelijk gedaan heb wat mij het beste leek met de beperkte ervaring die ik heb.

Er is een inlogscherm waar je kunt inloggen en als je je wachtwoord niet meer weet een nieuw wachtwoord aan kunt vragen, en er is een admin gedeelte dat controleert of de gebruiker rechten heeft om op de pagina te zijn, en waar de gebruiker zijn gegevens kan aanpassen.

Nu heb ik vanaf het begin af aan mijn hoofd zitten breken over de inlog pagina, omdat er zoveel opties zijn.

er kunnen een session variabelen zijn die gecontroleerd moeten worden (als er al ingelogd is) en er kunnen post variabelen zijn die gecontroleerd moeten worden (als er ingelogd word).

In beide gevallen kunnen zich dan de volgende situaties voordoen:

- email adres komt niet in db voor
- wachtwoord komt niet overeen met wachtwoord in db
- beide komen overeen en de gebruiker is ingelogd

Nu heb ik een abstract class LoginHandler gemaakt die het email adres en het wachtwoord controleert, waarbij de child classes voor post en session variabelen bepalen wat de uiteindelijke actie is die ondernomen moet worden.

Maar, als iemand zijn wachtwoord vergeten is en op de link 'wachtwoord vergeten' klikt, heb ik weer een deel van de functionaliteit van de LoginHandler class nodig. Voordat ik namelijk een nieuw wachtwoord aan kan maken en kan mailen, moet ik wel weten of het email adres in de database voorkomt. De functie daarvoor staat al in LoginHandler. Maar, in de child class zou ik dan maar 2 van de 4 abstract functions hoeven te gebruiken. Wat een foutmelding oplevert. Ik kan natuurlijk 2 lege functies in de child class zetten, maar deze hele situatie maakt dat ik me ernstig afvraag of ik het überhaupt niet helemaal verkeerd heb aangepakt.

Mijn vraag, lijkt dit ergens op en zo nee, hoe kan ik dit beter aanpakken?

De abstract class LoginHandler:


<?php
 abstract class LoginHandler {
	
	/**
 	 * ingevoerde gebruikersnaam
 	 */
 
	 private $invoeremail;
 
	/**
 	 * ingevoerd wachtwoord
 	 */
  
	 private $invoerwachtwoord;
	  
	/**
 	 * ingevoerde naam gebruiker
 	 */
   
	 private $db_handler; 

	/**
 	 * prefix die gebruikt wordt voor unieke tabelnamen in database, uit settings
 	 */
 
	 private $prefix; 

	/**
	 * constructor die de inloggegevens verwerkt
	 */
	 
	 public function __construct( $prefix, DatabaseHandler $db_handler, $invoeremail, $invoerwachtwoord ){
		  
		  $this->prefix = $prefix;
		  $this->db_handler = $db_handler;
		  $this->invoeremail = $invoeremail;
		  $this->invoerwachtwoord = $invoerwachtwoord;
		  
	 }
	 

	 /**
	  * controleer of het email adres voorkomt in de database
	  */
	 
	 public function check_login(){
		 
		$this->velden = array( "gebruikers_email", "gebruikers_ID" );
		
		$this->db_handler->select_single ( $this->prefix . 'Gebruikers', $this->velden, 'gebruikers_email', $this->invoeremail );
		$this->gebruikers_email = $this->db_handler->get_single( "gebruikers_email" ); 
	    $this->gebruikers_ID = $this->db_handler->get_single( "gebruikers_ID" ); 	
		
				if ( !empty( $this->gebruikers_email ) && !empty( $this->gebruikers_ID ) ){
				 
				 	//het email adres staat in de database, controleer nu het wachtwoord
				 	return $this->email_match(); 
	  
 		 		 } else {
	  			
					//het email adres staat niet in de database
	 	 			return $this->geen_email_match();
  				}
  
	 }//end function check_email()
	 
	 
	 /**
	  * controleer of het wacthwoord overeenkomt met het ingevulde wachtwoord
	  */	 
	 
	 public function check_wachtwoord() {
		 
		$this->velden = array( "gebruikers_email", "gebruikers_ID", "gebruikers_wachtwoord" );
		
		$this->db_handler->select_single ( $this->prefix . 'Gebruikers', $this->velden, 'gebruikers_email', $this->gebruikers_email );
		$this->gebruikers_wachtwoord = $this->db_handler->get_single( "gebruikers_wachtwoord" ); 
	    $this->gebruikers_ID = $this->db_handler->get_single( "gebruikers_ID" ); 	
		
				if ( password_verify( $this->invoerwachtwoord, $this->gebruikers_wachtwoord )) {
				
					//wachtwoorden komen overeen, stel de session variabelen in en redirect naar de juiste pagina
					return $this->inlog_match();
				
				} else {
				
					//wachtwoorden komen niet overeen
					return $this->geen_wachtwoord_match();	
				}		
			
	 }//end function check_wachtwoord()
	 
	 abstract function geen_email_match();
	 
	 abstract function email_match();
	 
	 abstract function inlog_match();
	 
	 abstract function geen_wachtwoord_match();
	 
	 
 }//end class Login_Handler
 
 ?>


Child class LoginHandlerWachtwoord (bij vergeten wachtwoord)


<?php
 /**
  * child class de uitvoer van een verloren wachtwoord regelt
  */
  class LoginHandlerWachtwoord extends LoginHandler {
	   
	   
	/**
 	 * als het email adres overeenkomt met dat in de database worden de gegevens opgeslagen en het wachtwoord hersteld
  	 */
	public function email_match(){
		
		$_SESSION[ 'vergetenwachtwoordid' ] = $this->gebruikers_ID;
		$_SESSION[ 'vergetenwachtwoordemail' ] = $this->gebruikers_email;
		
		$this->fout = 'wachtwoordherstellen.php';
		
		return $this->fout;
	
	}//end function email_match
	
	
	/**
 	 * als het email adres niet in de database voorkomt wordt het inlogscherm getoond met bijbehorende foutmelding
  	 */
 	public function geen_email_match(){
		
		$this->fout = '../index.php?fout=1';
		
		return $this->fout;
				
	}//end function geen_email_match
	
	private function inlog_match(){
		//lege functie omdat ik hem niet gebruikt in deze situatie
	}
	 
	private function geen_wachtwoord_match(){
		//lege functie omdat ik hem niet gebruikt in deze situatie
	}
	 
 }//end class LoginHandlerWacthwoord
 
 ?>
De abstract class wil vier situaties afgehandeld hebben: wel/geen geldig e-mailadres en wel/geen geldig wachtwoord. Die implementatie daarvan laat de abstract class echter over aan andere klassen. En dáár gaat het fout, omdat jij in je concrete toepassing wel de wel/geen geldig e-mailadres wilt afhandelen, maar niet de wel/geen geldig wachtwoord.

Eerste vraag is daarom: waarom wil je de toestanden wel/geen geldig wachtwoord niet implementeren, maar de twee toestanden wel/geen geldig e-mailadres wel? Mis je dan niet de helft van een login?
Er zijn nog twee child classes voor deze abstract class, class LoginHandlerSession (die het werk doet als er session variabelen gevonden worden:


<?php
  class LoginHandlerSession extends LoginHandler {
 	
	/*
 	 * als het email adres overeenkomt met dat in de database wordt het wachtwoord gecheckt (parent functie)
  	 */
 	public function email_match(){
		
		return $this->check_wachtwoord( $this->invoerwachtwoord );
	
	}//end function email_match
 	
	
	/**
 	 * als het email adres niet overeenkomt met dat in de database wordt het inlogscherm weer getoond met foutmelding
  	 */
	public function geen_email_match(){
		
		$this->fout = '../index.php?fout=3';
		
		return $this->fout;
				
	}//end function geen_email_match
	
	
 	/**
 	 * als het wachtwoord niet overeenkomt met dat in de database wordt het inlogscherm weer getoond met foutmelding
  	 */	
	public function geen_wachtwoord_match(){
		
		$this->fout = '../index.php?fout=3';
		
		return $this->fout;
		
	}//end function geen_wachtwoord_match


 	/**
 	 * als het email adres en het wachtwoord kloppen krijgt de gebruiker toegang
  	 */
	public function inlog_match(){
				
		return 1;
		
	}//end function geen_wachtwoord_match
	
 }//end class LoginHandlerSession
 
 
 ?>


en class LoginHandlerPost, die de afhandeling regelt als er gepost is vanaf het login formulier:


<?php
 class LoginHandlerPost extends LoginHandler {
 
 	/**
 	 * als het email adres overeenkomt met dat in de database wordt het wachtwoord gecheckt (parent functie)
  	 */
	public function email_match(){
		
		return $this->check_wachtwoord( $this->invoerwachtwoord );
	
	}//end function email_match
 
 
 	/**
 	 * als het email adres niet in de database voorkomt wordt het inlogscherm weer getoond met foutmelding
  	 */
	public function geen_email_match(){
		
		$this->fout = '../index.php?fout=1';
		
		return $this->fout;
				
	}//end function geen_email_match
 
 
 	/**
 	 * als het wachtwoord niet overeenkomt met dat in de database wordt het inlogscherm weer getoond met foutmelding
  	 */
	public function geen_wachtwoord_match(){
				
		$this->fout = '../index.php?fout=2';
		
		return $this->fout;
		
	}//end function geen_wachtwoord_match
 
 
 	/**
 	 * als het email adres en het wachtwoord kloppen worden de sessie variabelen ingesteld en krijgt de gebruiker
	 * dmv het instellen van de 1; toegang tot de admin
  	 */
	public function inlog_match(){
		
		$_SESSION[ 'loginemail' ] = $this->gebruikers_email;
		$_SESSION[ 'loginwachtwoord' ] = $this->invoerwachtwoord;
		$_SESSION[ 'gebruikers_ID' ] = $this->gebruikers_ID;
				
		return 1;
		
	}//end function geen_wachtwoord_match
	
	
 }//end class PostLoginHandler
?>



[size=xsmall]Toevoeging op 29/01/2017 16:12:20:[/size]

Als de gebruiker zijn wachtwoord kwijt is, dan hoeft het wachtwoord dus niet gecontroleerd te worden, alleen het email adres, voordat ik een nieuw wachtwoord aanmaak en mail
Marlies Maalderink op 29/01/2017 16:11:16

Als de gebruiker zijn wachtwoord kwijt is, dan hoeft het wachtwoord dus niet gecontroleerd te worden, alleen het email adres, voordat ik een nieuw wachtwoord aanmaak en mail

Klopt, dan is het geen login. En dus geen functionaliteit die een abstract class LoginHandler extendt, want er is geen login...

Je hebt dus een (abstract) class die te veel doet. Dat is een veelvoorkomend maar geen makkelijk probleem bij OOP programmeren.

Lang verhaal kort: je hebt die verificatie van een e-mailadres niet alleen nodig bij het inloggen maar ook bij een vergeten wachtwoord, dus je moet die refactoren naar iets dat op zich staat en wordt gebruikt door andere classes. De verificatie van een e-mailadres hoort niet thuis in de login zodra deze ook voor andere classes belangrijk is.
Dank je wel Ward. Pfff Ik vind het zo lastig om te bepalen hoe ik dit soort dingen vorm moet geven als er zoveel mogelijke situaties zijn.

Het verifiëren van het email adres en van het wachtwoord zal dan dus naar twee losse classes moeten. Maar je kan een child class geen gebruik laten maken van 2 parents. Mag ik vragen hoe jij dit zou structureren?
Zoals ik het zie doen die handlers iets met pagina(navigatie), formulieren en superglobals. En tegelijkertijd ben je bezig met het authenticeren van een gebruiker. Ik zou een user class aanmaken. Met ten minste twee methoden: een die een gebruiker authenticeert (invoer: gebruikersnaam, wachtwoord, uitvoer: een user id of false of wat dan ook) en een die een gebruiker inlogt (invoer: een user id, uitvoer: een boolean of het inloggen is geslaagd). Vervolgens onthoud je een user id in de sessie en herbouw je elke page-access het user object. Binnen je applicatie refereer je alleen aan dit user object, en niet aan je sessie of wat dan ook.
Dank je Thomas. Dat klinkt als een mooie manier om het aan te pakken. Volgens mij is het ook niet zo netjes/handig om het instellen van sessionvariabelen in deze class zelf te doen. Bovendien wil ik de beveiliging van de sessions nog uitbreiden en dan is het ook niet handig als dit aan de user class vast zit.

Overigens las ik gisteren (ik geloof hier op het forum ergens maar misschien was het ook wel ergens anders) dat het sowieso niet zo slim is om bij het inloggen een aparte foutmelding weer te geven voor 'email adres komt niet voor' en voor 'wachtwoord klopt niet' dus dat maakt het aantal mogelijke uitvoer opties ook weer kleiner. Je hebt dan nog maar 2 opties. Inloggen is gelukt of inloggen is niet gelukt...

Reageren