Kimlik Doğrulama

Nette, sitelerimizde kimlik doğrulamayı gerçekleştirmenin bir yolunu sunar, ama belirli bir yaklaşımı dayatmaz. Gerçekleştirim tümüyle bize kalmıştır. Nette, kullanıcıyı seçtiğimiz herhangi bir yolla doğrulamak için yalnızca tek bir metot, authenticate, gerektiren Nette\Security\Authenticator arayüzünü içerir.

Bir kullanıcının kimliğinin doğrulanabileceği pek çok yol vardır. En yaygın yöntem parola tabanlı kimlik doğrulamadır (kullanıcı adını ya da e-postasını ve bir parola verir), ama başka yöntemler de vardır. Bazı sitelerdeki “Facebook ile giriş yap” düğmelerini ya da Google/Twitter/GitHub üzerinden girişi biliyor olabilirsiniz. Nette ile herhangi bir giriş yöntemini kullanabilir, hatta bunları birleştirebiliriz. Karar tümüyle bize aittir.

Genellikle kendi doğrulayıcımızı yazarız. Ancak bu basit blog için, yapılandırma dosyasında saklanan bir kullanıcı adı ve parolaya göre giriş yapan yerleşik doğrulayıcıyı kullanacağız. Bu, sınama amaçları için uygundur. config/common.neon yapılandırma dosyasına şu security bölümünü ekleyelim:

security:
	users:
		admin: secret  # kullanıcı 'admin', parola 'secret'

Nette, bu doğrulayıcı için DI container'da otomatik olarak bir servis oluşturur.

Giriş Formu

Kimlik doğrulama ayarlandığına göre, giriş yapmak için bir kullanıcı arayüzü oluşturmamız gerekiyor. Şunları yapacak, SignPresenter adında yeni bir presenter oluşturalım:

  • bir giriş formu göstermek (kullanıcı adı ve parola alanlarıyla)
  • form gönderildiğinde kullanıcının kimliğini doğrulamak
  • bir çıkış seçeneği sunmak

Giriş formuyla başlayacağız. Formların presenter'larda nasıl çalıştığını zaten biliyoruz. SignPresenter sınıfını oluşturup createComponentSignInForm metodunu yazalım. Şuna benzer görünmelidir:

<?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', 'Kullanıcı adı:')
			->setRequired('Lütfen kullanıcı adınızı girin.');

		$form->addPassword('password', 'Parola:')
			->setRequired('Lütfen parolanızı girin.');

		$form->addSubmit('send', 'Giriş yap');

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

Kullanıcı adı ve parola için alanlar var.

Şablon

Form, in.latte şablonunda render edilecek:

{block content}
<h1 n:block=title>Giriş yap</h1>

{control signInForm}

Giriş Callback'i

Sırada, kullanıcının girişini yapacak ve form başarıyla gönderildikten hemen sonra çağrılacak callback metodunu ekleyeceğiz.

Callback, yalnızca kullanıcının girdiği kullanıcı adını ve parolayı alıp doğrulayıcıya aktarır. Başarılı girişten sonra ana sayfaya yönlendiririz.

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('Kullanıcı adı ya da parola hatalı.');
	}
}

User::login() metodu, kullanıcı adı ve parola yapılandırma dosyasındaki kimlik bilgileriyle eşleşmiyorsa bir istisna fırlatır. Bildiğimiz gibi bu, Tracy'nin kırmızı ekranına ya da üretim kipinde bir sunucu hatası mesajına yol açabilir. Bunu istemiyoruz. Bu yüzden istisnayı yakalar ve forma güzel, kullanıcı dostu bir hata mesajı ekleriz.

Bir form hatası oluştuğunda, formu içeren sayfa yeniden render edilir ve formun üstünde kullanıcıya yanlış kullanıcı adı ya da parola girdiğini bildiren bir mesaj çıkar.

Presenter'ları Güvenli Kılma

EditPresenter içinde tanımlı olan, gönderi ekleme ve düzenleme formunu güvenli kılacağız. Amaç, giriş yapmamış kullanıcıların sayfaya erişmesini engellemektir.

Presenter yaşam döngüsünün en başında hemen çalışan bir startup() metodu oluşturacağız. Bu metot, giriş yapmamış kullanıcıları giriş formuna yönlendirecek.

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

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

Bağlantıları Gizleme

Yetkisiz bir kullanıcı artık create ya da edit sayfalarını göremiyor, ama onlara işaret eden bağlantıları hâlâ görebiliyor. Bunları da gizlemeliyiz. Böyle bir bağlantı app/Presentation/Home/default.latte şablonunda bulunuyor ve yalnızca giriş yapmış kullanıcılara görünmeli.

Onu, n:if adlı bir n:attribute kullanarak gizleyebiliriz. Koşul false ise, içeriğiyle birlikte <a> etiketinin tamamı gösterilmez.

<a n:href="Edit:create" n:if="$user->isLoggedIn()">Gönderi oluştur</a>

bu, şu yazımın kısayoludur (onu tag-if ile karıştırmayın):

{if $user->isLoggedIn()}<a n:href="Edit:create">Gönderi oluştur</a>{/if}

app/Presentation/Post/show.latte şablonundaki düzenleme bağlantısını da aynı şekilde gizleyin.

Giriş Bağlantısı

Giriş sayfasına aslında nasıl ulaşacağız? Ona giden bir bağlantı yok. Hadi @layout.latte şablonuna bir tane ekleyelim. Uygun bir yer bulmayı deneyin; neredeyse her yerde olabilir.

...
<ul class="navig">
	<li><a n:href="Home:">Ana sayfa</a></li>
	{if $user->isLoggedIn()}
		<li><a n:href="Sign:out">Çıkış yap</a></li>
	{else}
		<li><a n:href="Sign:in">Giriş yap</a></li>
	{/if}
</ul>
...

Kullanıcı giriş yapmamışsa “Giriş yap” bağlantısı gösterilir. Aksi durumda “Çıkış yap” bağlantısı gösterilir. Bu eylemi de SignPresenter sınıfına ekleyeceğiz.

Kullanıcıyı çıkıştan hemen sonra yönlendirdiğimizden, bir şablona gerek yok. Çıkış eylemi şöyle görünür:

public function actionOut(): void
{
	$this->getUser()->logout();
	$this->flashMessage('Çıkış yaptınız.');
	$this->redirect('Home:');
}

Yalnızca logout() metodunu çağırır ve ardından kullanıcıya bir onay mesajı gösterir.

Özet

Kullanıcının girişi ve çıkışı için bağlantılarımız var. Kimlik doğrulama için yerleşik doğrulayıcıyı kullandık ve bu basit bir sınama uygulaması olduğundan giriş bilgileri yapılandırma dosyasında duruyor. Ayrıca düzenleme formlarını, yalnızca giriş yapmış kullanıcılar gönderi ekleyip düzenleyebilsin diye güvenli kıldık.

Kullanıcı girişi ve yetkilendirme hakkında daha fazlasını burada okuyabilirsiniz.