Authentication
Το Nette παρέχει έναν τρόπο προγραμματισμού της αυθεντικοποίησης στις
σελίδες μας, αλλά δεν μας επιβάλλει τίποτα. Η υλοποίηση εξαρτάται
αποκλειστικά από εμάς. Το Nette περιέχει το interface Nette\Security\Authenticator
,
το οποίο απαιτεί μόνο μία μέθοδο authenticate
, η οποία επαληθεύει τον
χρήστη με οποιονδήποτε τρόπο θέλουμε.
Υπάρχουν πολλές δυνατότητες για τον τρόπο με τον οποίο μπορεί να επαληθευτεί ένας χρήστης. Ο πιο συνηθισμένος τρόπος επαλήθευσης είναι μέσω κωδικού πρόσβασης (ο χρήστης παρέχει το όνομά του ή το e-mail και τον κωδικό πρόσβασης), αλλά υπάρχουν και άλλοι τρόποι. Ίσως γνωρίζετε τα κουμπιά τύπου “Σύνδεση μέσω Facebook” ή τη σύνδεση μέσω Google/Twitter/GitHub σε ορισμένες σελίδες. Με το Nette μπορούμε να έχουμε οποιαδήποτε μέθοδο σύνδεσης ή μπορούμε ακόμη και να τις συνδυάσουμε. Εξαρτάται αποκλειστικά από εμάς.
Συνήθως θα γράφαμε τον δικό μας authenticator, αλλά για αυτό το απλό μικρό blog
θα χρησιμοποιήσουμε τον ενσωματωμένο authenticator, ο οποίος συνδέει βάσει
κωδικού πρόσβασης και ονόματος χρήστη που είναι αποθηκευμένα στο
αρχείο διαμόρφωσης. Είναι χρήσιμος για δοκιμαστικούς σκοπούς.
Προσθέτουμε λοιπόν την ακόλουθη ενότητα security
στο αρχείο
διαμόρφωσης config/common.neon
:
security:
users:
admin: secret # χρήστης 'admin', κωδικός 'secret'
Το Nette δημιουργεί αυτόματα μια υπηρεσία στον DI container.
Φόρμα σύνδεσης
Τώρα έχουμε έτοιμη την αυθεντικοποίηση και πρέπει να προετοιμάσουμε
το περιβάλλον χρήστη για τη σύνδεση. Ας δημιουργήσουμε λοιπόν έναν νέο
presenter με το όνομα SignPresenter
, ο οποίος:
- εμφανίζει τη φόρμα σύνδεσης (με όνομα χρήστη και κωδικό πρόσβασης)
- μετά την υποβολή της φόρμας, επαληθεύει τον χρήστη
- παρέχει τη δυνατότητα αποσύνδεσης
Θα ξεκινήσουμε με τη φόρμα σύνδεσης. Γνωρίζουμε ήδη πώς λειτουργούν
οι φόρμες στους presenters. Θα δημιουργήσουμε λοιπόν τον presenter
SignPresenter
και θα γράψουμε τη μέθοδο createComponentSignInForm
. Θα
πρέπει να μοιάζει κάπως έτσι:
<?php
namespace App\Presentation\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', 'Όνομα χρήστη:')
->setRequired('Παρακαλώ συμπληρώστε το όνομα χρήστη σας.');
$form->addPassword('password', 'Κωδικός πρόσβασης:')
->setRequired('Παρακαλώ συμπληρώστε τον κωδικό πρόσβασής σας.');
$form->addSubmit('send', 'Σύνδεση');
$form->onSuccess[] = $this->signInFormSucceeded(...);
return $form;
}
}
Υπάρχουν πεδία για το όνομα χρήστη και τον κωδικό πρόσβασης.
Template
Η φόρμα θα αποδοθεί στο πρότυπο in.latte
:
{block content}
<h1 n:block=title>Σύνδεση</h1>
{control signInForm}
Callback σύνδεσης
Στη συνέχεια, θα συμπληρώσουμε το callback για τη σύνδεση του χρήστη, το οποίο θα κληθεί αμέσως μετά την επιτυχή υποβολή της φόρμας.
Το callback απλώς λαμβάνει το όνομα χρήστη και τον κωδικό πρόσβασης που συμπλήρωσε ο χρήστης και τα παραδίδει στον 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('Λανθασμένο όνομα χρήστη ή κωδικός πρόσβασης.');
}
}
Η μέθοδος User::login() πετάει μια εξαίρεση αν το όνομα χρήστη και ο κωδικός πρόσβασης δεν συμφωνούν με τα στοιχεία στο αρχείο διαμόρφωσης. Όπως ήδη γνωρίζουμε, αυτό μπορεί να οδηγήσει σε μια κόκκινη σελίδα σφάλματος ή, σε κατάσταση παραγωγής, σε ένα μήνυμα που ενημερώνει για σφάλμα διακομιστή. Αυτό όμως δεν το θέλουμε. Γι' αυτό πιάνουμε αυτή την εξαίρεση και παραδίδουμε ένα όμορφο, φιλικό προς τον χρήστη μήνυμα σφάλματος στη φόρμα.
Μόλις προκύψει σφάλμα στη φόρμα, η σελίδα με τη φόρμα επανασχεδιάζεται και πάνω από τη φόρμα εμφανίζεται ένα όμορφο μήνυμα που ενημερώνει τον χρήστη ότι συμπλήρωσε λάθος όνομα χρήστη ή κωδικό πρόσβασης.
Ασφάλεια των presenters
Θα ασφαλίσουμε τη φόρμα για την προσθήκη και την επεξεργασία
αναρτήσεων. Αυτή ορίζεται στον presenter EditPresenter
. Ο στόχος είναι να
αποτρέψουμε την πρόσβαση στη σελίδα σε χρήστες που δεν είναι
συνδεδεμένοι.
Θα δημιουργήσουμε τη μέθοδο startup()
, η οποία εκτελείται αμέσως
στην αρχή του κύκλου
ζωής του presenter. Αυτή ανακατευθύνει τους μη συνδεδεμένους χρήστες στη
φόρμα σύνδεσης.
public function startup(): void
{
parent::startup();
if (!$this->getUser()->isLoggedIn()) {
$this->redirect('Sign:in');
}
}
Απόκρυψη συνδέσμων
Ο μη εξουσιοδοτημένος χρήστης δεν μπορεί πλέον να δει τη σελίδα
create
ή edit
, αλλά μπορεί ακόμα να δει τους συνδέσμους προς
αυτές. Θα πρέπει να τους κρύψουμε επίσης. Ένας τέτοιος σύνδεσμος
βρίσκεται στο πρότυπο app/Presentation/Home/default.latte
και θα πρέπει να τον
βλέπουν μόνο οι συνδεδεμένοι χρήστες.
Μπορούμε να τον κρύψουμε χρησιμοποιώντας ένα n:attribute με το όνομα
n:if
. Αν αυτή η συνθήκη είναι false
, ολόκληρη η ετικέτα
<a>
, συμπεριλαμβανομένου του περιεχομένου,
παραμένει κρυφή.
<a n:href="Edit:create" n:if="$user->isLoggedIn()">Δημιουργία ανάρτησης</a>
που είναι συντομογραφία της ακόλουθης γραφής (μην το συγχέετε με το
tag-if
):
{if $user->isLoggedIn()}<a n:href="Edit:create">Δημιουργία ανάρτησης</a>{/if}
Με τον ίδιο τρόπο θα κρύψουμε επίσης τον σύνδεσμο στο πρότυπο
app/Presentation/Post/show.latte
.
Σύνδεσμος σύνδεσης
Πώς θα φτάσουμε στην πραγματικότητα στη σελίδα σύνδεσης; Δεν υπάρχει
κανένας σύνδεσμος που να οδηγεί σε αυτήν. Ας τον προσθέσουμε λοιπόν στο
πρότυπο @layout.latte
. Προσπαθήστε να βρείτε ένα κατάλληλο μέρος –
μπορεί να είναι σχεδόν οπουδήποτε.
...
<ul class="navig">
<li><a n:href="Home:">Άρθρα</a></li>
{if $user->isLoggedIn()}
<li><a n:href="Sign:out">Αποσύνδεση</a></li>
{else}
<li><a n:href="Sign:in">Σύνδεση</a></li>
{/if}
</ul>
...
Αν ο χρήστης δεν είναι συνδεδεμένος, εμφανίζεται ο σύνδεσμος
“Σύνδεση”. Σε αντίθετη περίπτωση, εμφανίζεται ο σύνδεσμος
“Αποσύνδεση”. Αυτή την ενέργεια θα την προσθέσουμε επίσης στον
SignPresenter
.
Δεδομένου ότι ανακατευθύνουμε αμέσως τον χρήστη μετά την αποσύνδεση, δεν χρειάζεται κανένα πρότυπο. Η αποσύνδεση μοιάζει κάπως έτσι:
public function actionOut(): void
{
$this->getUser()->logout();
$this->flashMessage('Η αποσύνδεση ήταν επιτυχής.');
$this->redirect('Home:');
}
Απλώς καλείται η μέθοδος logout()
και στη συνέχεια εμφανίζεται
ένα όμορφο μήνυμα που επιβεβαιώνει την επιτυχή αποσύνδεση.
Περίληψη
Έχουμε έναν σύνδεσμο για τη σύνδεση και επίσης την αποσύνδεση του χρήστη. Για την επαλήθευση χρησιμοποιήσαμε τον ενσωματωμένο authenticator και τα στοιχεία σύνδεσης τα έχουμε στο αρχείο διαμόρφωσης, καθώς πρόκειται για μια απλή δοκιμαστική εφαρμογή. Επίσης, ασφαλίσαμε τις φόρμες επεξεργασίας, ώστε μόνο οι συνδεδεμένοι χρήστες να μπορούν να προσθέτουν και να επεξεργάζονται αναρτήσεις.
Εδώ μπορείτε να διαβάσετε περισσότερα για το user login και την Authorization.