Αυθεντικοποίηση

Η Nette σας παρέχει οδηγίες για το πώς να προγραμματίσετε τον έλεγχο ταυτότητας στη σελίδα σας, αλλά δεν σας υποχρεώνει να το κάνετε με κάποιον συγκεκριμένο τρόπο. Η υλοποίηση εξαρτάται από εσάς. Η Nette έχει μια διεπαφή Nette\Security\Authenticator η οποία σας αναγκάζει να υλοποιήσετε μόνο μια μοναδική μέθοδο που ονομάζεται authenticate, η οποία βρίσκει τον χρήστη όπως εσείς θέλετε.

Υπάρχουν πολλοί τρόποι με τους οποίους ένας χρήστης μπορεί να πιστοποιήσει τον εαυτό του. Ο πιο συνηθισμένος τρόπος είναι ο πιστοποίηση με βάση τον κωδικό πρόσβασης (ο χρήστης παρέχει το όνομά του ή το email του και έναν κωδικό πρόσβασης), αλλά υπάρχουν και άλλοι τρόποι. Μπορεί να σας είναι γνωστά τα κουμπιά “Σύνδεση με το Facebook” σε πολλούς ιστότοπους, ή σύνδεση μέσω Google/Twitter/GitHub ή οποιουδήποτε άλλου ιστότοπου. Με τη Nette, μπορείτε να έχετε οποιαδήποτε μέθοδο ελέγχου ταυτότητας θέλετε, ή μπορείτε να τις συνδυάσετε. Εξαρτάται από εσάς.

Κανονικά θα γράφατε το δικό σας authenticator, αλλά για αυτό το απλό μικρό blog θα χρησιμοποιήσουμε τον ενσωματωμένο authenticator, ο οποίος αυθεντικοποιείται με βάση έναν κωδικό πρόσβασης και ένα όνομα χρήστη που είναι αποθηκευμένα σε ένα αρχείο ρυθμίσεων. Είναι καλός για δοκιμαστικούς σκοπούς. Έτσι, θα προσθέσουμε την ακόλουθη ενότητα security στο αρχείο ρυθμίσεων config/common.neon:

security:
	users:
		admin: secret  # χρήστης 'admin', κωδικός πρόσβασης 'secret'

Η Nette θα δημιουργήσει αυτόματα μια υπηρεσία στο δοχείο DI.

Φόρμα εγγραφής

Τώρα έχουμε έτοιμο το backend μέρος του ελέγχου ταυτότητας και πρέπει να παρέχουμε μια διεπαφή χρήστη, μέσω της οποίας ο χρήστης θα συνδεθεί. Ας δημιουργήσουμε έναν νέο παρουσιαστή με όνομα SignPresenter, ο οποίος θα

  • εμφανίζει μια φόρμα σύνδεσης (ζητώντας όνομα χρήστη και κωδικό πρόσβασης)
  • θα πιστοποιεί τον χρήστη όταν υποβληθεί η φόρμα
  • παρέχει ενέργεια αποσύνδεσης

Ας ξεκινήσουμε με τη φόρμα σύνδεσης. Γνωρίζετε ήδη πώς λειτουργούν οι φόρμες σε έναν παρουσιαστή. Δημιουργήστε την SignPresenter και τη μέθοδο createComponentSignInForm. Θα πρέπει να μοιάζει με αυτό:

<?php
namespace App\UI\Sign;

use Nette;
use Nette\Application\UI\Form;

final class SignPresenter extends Nette\Application\UI\Presenter
{
	protected function createComponentSignInForm(): Form
	{
		$form = new Form;
		$form->addText('username', 'Username:')
			->setRequired('Please enter your username.');

		$form->addPassword('password', 'Password:')
			->setRequired('Please enter your password.');

		$form->addSubmit('send', 'Sign in');

		$form->onSuccess[] = $this->signInFormSucceeded(...);
		return $form;
	}
}

Υπάρχει μια είσοδος για το όνομα χρήστη και τον κωδικό πρόσβασης.

Πρότυπο

Η φόρμα θα εμφανιστεί στο πρότυπο in.latte

{block content}
<h1 n:block=title>Sign in</h1>

{control signInForm}

Χειριστής σύνδεσης

Προσθέτουμε επίσης έναν χειριστή φόρμας για την εγγραφή του χρήστη, ο οποίος καλείται αμέσως μετά την υποβολή της φόρμας.

Ο χειριστής θα λάβει απλώς το όνομα χρήστη και τον κωδικό πρόσβασης που εισήγαγε ο χρήστης και θα τα περάσει στον authenticator που ορίστηκε νωρίτερα. Αφού ο χρήστης συνδεθεί, θα τον ανακατευθύνουμε στην αρχική σελίδα.

private function signInFormSucceeded(Form $form, \stdClass $data): void
{
	try {
		$this->getUser()->login($data->username, $data->password);
		$this->redirect('Home:');

	} catch (Nette\Security\AuthenticationException $e) {
		$form->addError('Incorrect username or password.');
	}
}

Η μέθοδος User::login() θα πρέπει να πετάει μια εξαίρεση όταν το όνομα χρήστη ή ο κωδικός πρόσβασης δεν ταιριάζει με αυτά που έχουμε ορίσει νωρίτερα. Όπως ήδη γνωρίζουμε, αυτό θα είχε ως αποτέλεσμα μια κόκκινη οθόνη του Tracy ή, σε λειτουργία παραγωγής, ένα μήνυμα που θα ενημέρωνε για ένα εσωτερικό σφάλμα του διακομιστή. Αυτό δεν θα μας άρεσε. Γι' αυτό πιάνουμε την εξαίρεση και προσθέτουμε ένα ωραίο και φιλικό μήνυμα σφάλματος στη φόρμα.

Όταν το σφάλμα εμφανιστεί στη φόρμα, η σελίδα με τη φόρμα θα αποδοθεί ξανά και πάνω από τη φόρμα θα υπάρχει ένα ωραίο μήνυμα, που θα ενημερώνει τον χρήστη ότι έχει εισάγει λάθος όνομα χρήστη ή κωδικό πρόσβασης.

Ασφάλεια των παρουσιαστών

Θα εξασφαλίσουμε μια φόρμα για την προσθήκη και την επεξεργασία αναρτήσεων. Ορίζεται στον παρουσιαστή EditPresenter. Ο στόχος είναι να αποτρέψουμε την πρόσβαση στη σελίδα από χρήστες που δεν είναι συνδεδεμένοι.

Δημιουργούμε μια μέθοδο startup() που ξεκινάει αμέσως στην αρχή του κύκλου ζωής του παρουσιαστή. Αυτή ανακατευθύνει τους μη συνδεδεμένους χρήστες στη φόρμα σύνδεσης.

public function startup(): void
{
	parent::startup();

	if (!$this->getUser()->isLoggedIn()) {
		$this->redirect('Sign:in');
	}
}

Ένας μη πιστοποιημένος χρήστης δεν μπορεί πλέον να δει τη σελίδα δημιουργίας ή επεξεργασίας, αλλά μπορεί ακόμα να δει τους συνδέσμους που οδηγούν σε αυτές. Ας αποκρύψουμε και αυτούς. Ένας τέτοιος σύνδεσμος βρίσκεται στη διεύθυνση app/UI/Home/default.latte, και θα πρέπει να είναι ορατός μόνο αν ο χρήστης είναι συνδεδεμένος.

Μπορούμε να τον αποκρύψουμε χρησιμοποιώντας το n:attribute που ονομάζεται n:if. Εάν η δήλωση μέσα σε αυτό είναι false, ολόκληρο το <a> ετικέτα και τα περιεχόμενά της δεν θα εμφανίζονται:

<a n:href="Edit:create" n:if="$user->isLoggedIn()">Create post</a>

(μην το συγχέετε με το tag-if):

{if $user->isLoggedIn()}<a n:href="Edit:create">Create post</a>{/if}

Θα πρέπει να αποκρύψετε το σύνδεσμο επεξεργασίας που βρίσκεται στο app/UI/Post/show.latte με παρόμοιο τρόπο.

Γεια σας, αλλά πώς μπορούμε να φτάσουμε στη σελίδα σύνδεσης; Δεν υπάρχει κανένας σύνδεσμος που να οδηγεί σε αυτήν. Ας προσθέσουμε έναν στο αρχείο προτύπου @layout.latte. Προσπαθήστε να βρείτε ένα ωραίο μέρος, μπορεί να είναι οπουδήποτε σας αρέσει περισσότερο.

...
<ul class="navig">
	<li><a n:href="Home:">Home</a></li>
	{if $user->isLoggedIn()}
		<li><a n:href="Sign:out">Sign out</a></li>
	{else}
		<li><a n:href="Sign:in">Sign in</a></li>
	{/if}
</ul>
...

Αν ο χρήστης δεν έχει συνδεθεί ακόμα, θα εμφανίσουμε το σύνδεσμο “Σύνδεση”. Διαφορετικά, θα εμφανιστεί ο σύνδεσμος “Είσοδος”. Προσθέτουμε αυτή την ενέργεια στο SignPresenter.

Η ενέργεια αποσύνδεσης μοιάζει με αυτή, και επειδή ανακατευθύνουμε τον χρήστη αμέσως, δεν υπάρχει ανάγκη για ένα πρότυπο προβολής.

public function actionOut(): void
{
	$this->getUser()->logout();
	$this->flashMessage('You have been signed out.');
	$this->redirect('Home:');
}

Απλά καλεί τη μέθοδο logout() και στη συνέχεια εμφανίζει ένα ωραίο μήνυμα στο χρήστη.

Περίληψη

Έχουμε έναν σύνδεσμο για να συνδεθούμε και να αποσυνδεθούμε από τον χρήστη. Έχουμε χρησιμοποιήσει τον ενσωματωμένο αυθεντικοποιητή για τον έλεγχο ταυτότητας και τα στοιχεία σύνδεσης βρίσκονται στο αρχείο ρυθμίσεων, καθώς πρόκειται για μια απλή δοκιμαστική εφαρμογή. Έχουμε επίσης εξασφαλίσει τις φόρμες επεξεργασίας ώστε μόνο οι συνδεδεμένοι χρήστες να μπορούν να προσθέτουν και να επεξεργάζονται αναρτήσεις.

Εδώ μπορείτε να διαβάσετε περισσότερα σχετικά με τη σύνδεση και την εξουσιοδότηση χρηστών.