Kullanıcıların Kimliğini Doğrulama
Web uygulamalarının kullanıcı girişi veya kullanıcı ayrıcalıklarını kontrol etmek için herhangi bir mekanizmaya ihtiyacı yoktur. Bu bölümde, aşağıdakiler hakkında konuşacağız:
- kullanıcı girişi ve çıkışı
- özel doğrulayıcılar ve yetkilendiriciler
Örneklerde, mevcut kullanıcıyı temsil eden ve bağımlılık enjeksiyonu kullanarak geçirerek
elde ettiğiniz Nette\Security\User sınıfından bir
nesne kullanacağız. Sunucularda $user = $this->getUser()
adresini çağırmanız yeterlidir.
Kimlik Doğrulama
Kimlik doğrulama, kullanıcı girişi, yani bir kullanıcının kimliğinin doğrulandığı süreç anlamına gelir.
Kullanıcı genellikle kullanıcı adı ve parola kullanarak kendini tanıtır. Doğrulama, sözde kimlik doğrulayıcı tarafından gerçekleştirilir. Oturum açma başarısız olursa,
Nette\Security\AuthenticationException
atar.
try {
$user->login($username, $password);
} catch (Nette\Security\AuthenticationException $e) {
$this->flashMessage('The username or password you entered is incorrect.');
}
Kullanıcının oturumu bu şekilde kapatılır:
$user->logout();
Ve kullanıcının oturum açıp açmadığını kontrol eder:
echo $user->isLoggedIn() ? 'yes' : 'no';
Basit, değil mi? Ve tüm güvenlik hususları sizin için Nette tarafından ele alınır.
Presenter'da, startup()
yönteminde oturum açmayı doğrulayabilir ve oturum açmamış bir kullanıcıyı oturum
açma sayfasına yönlendirebilirsiniz.
protected function startup()
{
parent::startup();
if (!$this->getUser()->isLoggedIn()) {
$this->redirect('Sign:in');
}
}
Son kullanma tarihi
Kullanıcı oturumu, genellikle bir oturum olan deponun sona ermesiyle birlikte sona
erer ( oturum sona erme ayarına bakın). Ancak,
kullanıcının oturumunun kapatılacağı daha kısa bir zaman aralığı da belirleyebilirsiniz. Bu amaçla login()
adresinden önce çağrılan setExpiration()
yöntemi kullanılır. Parametre olarak göreli zaman içeren bir
dize girin:
// oturum açma 30 dakika işlem yapılmadığında sona erer
$user->setExpiration('30 minutes');
// setin sona ermesini iptal et
$user->setExpiration(null);
$user->getLogoutReason()
yöntemi, zaman aralığı sona erdiği için kullanıcının oturumunun kapatılıp
kapatılmadığını belirtir. Süre dolmuşsa Nette\Security\UserStorage::LogoutInactivity
sabitini veya
logout()
yöntemi çağrıldığında UserStorage::LogoutManual
sabitini döndürür.
Kimlik Doğrulayıcı
Oturum açma verilerini, yani genellikle ad ve parolayı doğrulayan bir nesnedir. Önemsiz uygulama, yapılandırmada tanımlanabilen Nette\Security\SimpleAuthenticator sınıfıdır:
security:
users:
# name: password
johndoe: secret123
kathy: evenmoresecretpassword
Bu çözüm test amaçları için daha uygundur. Size bir veritabanı tablosuna karşı kimlik bilgilerini doğrulayacak bir kimlik doğrulayıcının nasıl oluşturulacağını göstereceğiz.
Kimlik doğrulayıcı, Nette\Security\Authenticator arayüzünü
authenticate()
yöntemiyle uygulayan bir nesnedir. Görevi ya sözde kimliği döndürmek
ya da bir istisna fırlatmaktır Nette\Security\AuthenticationException
. Ayrıca
Authenticator::IdentityNotFound
veya Authenticator::InvalidCredential
ince taneli bir hata kodu
sağlamak da mümkün olabilir.
use Nette;
use Nette\Security\SimpleIdentity;
class MyAuthenticator implements Nette\Security\Authenticator
{
public function __construct(
private Nette\Database\Explorer $database,
private Nette\Security\Passwords $passwords,
) {
}
public function authenticate(string $username, string $password): SimpleIdentity
{
$row = $this->database->table('users')
->where('username', $username)
->fetch();
if (!$row) {
throw new Nette\Security\AuthenticationException('Kullanıcı bulunamadı.');
}
if (!$this->passwords->verify($password, $row->password)) {
throw new Nette\Security\AuthenticationException('Geçersiz şifre.');
}
return new SimpleIdentity(
$row->id,
$row->role, // veya rol dizisi
['name' => $row->username],
);
}
}
MyAuthenticator sınıfı, Nette Database Explorer aracılığıyla
veritabanı ile iletişim kurar ve username
sütununun kullanıcının oturum açma adını ve password
sütununun hash'i içerdiği users
tablosu ile
çalışır. Adı ve parolayı doğruladıktan sonra, kullanıcının kimliğini, daha sonra
bahsedeceğimiz rolünü (tablodaki role
sütunu) ve ek verileri içeren bir diziyi (bizim durumumuzda kullanıcı
adı) döndürür.
Kimlik doğrulayıcıyı DI konteynerinin bir servisi olarak yapılandırmaya ekleyeceğiz:
services:
- MyAuthenticator
$onLoggedIn, $onLoggedOut Olayları
Object Nette\Security\User
, $onLoggedIn
ve $onLoggedOut
olaylarına sahiptir, böylece başarılı bir oturum açma işleminden
sonra veya kullanıcı oturumu kapattıktan sonra tetiklenen geri aramalar ekleyebilirsiniz.
$user->onLoggedIn[] = function () {
// kullanıcı yeni giriş yaptı
};
Kimlik
Kimlik, bir kullanıcı hakkında kimlik doğrulayıcı tarafından döndürülen ve daha sonra bir oturumda saklanan ve
$user->getIdentity()
kullanılarak alınan bir dizi bilgidir. Böylece kimlik doğrulayıcıda ilettiğimiz gibi
kimliği, rolleri ve diğer kullanıcı verilerini alabiliriz:
$user->getIdentity()->getId();
// ayrıca $user->getId() kısayolu da çalışır;
$user->getIdentity()->getRoles();
// kullanıcı verilerine özellikler olarak erişilebilir
// MyAuthenticator'da aktardığımız isim
$user->getIdentity()->name;
Önemli olarak, kullanıcı $user->logout()
adresini kullanarak oturumu kapattığında kimlik silinmez
ve hala kullanılabilir durumdadır. Dolayısıyla, kimlik varsa, tek başına kullanıcının da oturum açmış olduğunu
göstermez. Eğer kimliği açıkça silmek istiyorsak, logout(true)
adresinden kullanıcının oturumunu
kapatırız.
Bu sayede, hangi kullanıcının bilgisayar başında olduğunu varsayabilir ve örneğin e-mağazada kişiselleştirilmiş teklifler görüntüleyebilirsiniz, ancak kişisel verilerini yalnızca oturum açtıktan sonra görüntüleyebilirsiniz.
Identity, Nette\Security\IIdentity arayüzünü uygulayan bir nesnedir, varsayılan uygulama Nette\Security\SimpleIdentity şeklindedir. Ve belirtildiği gibi, kimlik oturumda saklanır, bu nedenle, örneğin, oturum açan kullanıcılardan bazılarının rolünü değiştirirsek, eski veriler tekrar oturum açana kadar kimlikte tutulur.
Oturum Açan Kullanıcı için Depolama
Kullanıcı hakkındaki iki temel bilgi, yani oturum açıp açmadığı ve kimliği, genellikle
oturumda taşınır. Bu bilgiler değiştirilebilir. Bu bilgilerin saklanması için Nette\Security\UserStorage
arayüzünü uygulayan bir nesne sorumludur. İki standart uygulama vardır; birincisi verileri bir oturumda, ikincisi ise bir
çerezde iletir. Bunlar Nette\Bridges\SecurityHttp\SessionStorage
ve CookieStorage
sınıflarıdır.
Depolamayı seçebilir ve güvenlik › kimlik doğrulama
yapılandırmasında çok uygun bir şekilde yapılandırabilirsiniz.
Ayrıca kimlik kaydetme (uyku) ve geri yüklemenin (uyanma) tam olarak nasıl gerçekleşeceğini de kontrol
edebilirsiniz. İhtiyacınız olan tek şey kimlik doğrulayıcının Nette\Security\IdentityHandler
arayüzünü
uygulamasıdır. Bunun iki yöntemi vardır: sleepIdentity()
kimlik depoya yazılmadan önce ve
wakeupIdentity()
kimlik okunduktan sonra çağrılır. Yöntemler kimliğin içeriğini değiştirebilir veya dönen
yeni bir nesneyle değiştirebilir. wakeupIdentity()
yöntemi, kullanıcının oturumunu kapatan null
yöntemini bile döndürebilir.
Örnek olarak, bir oturumdan geri yüklendikten hemen sonra kimlik rollerinin nasıl güncelleneceğine ilişkin yaygın bir
soruya bir çözüm göstereceğiz. wakeupIdentity()
yönteminde, örneğin veritabanından mevcut rolleri kimliğe
aktarıyoruz:
final class Authenticator implements
Nette\Security\Authenticator, Nette\Security\IdentityHandler
{
public function sleepIdentity(IIdentity $identity): IIdentity
{
// burada oturum açtıktan sonra depolamadan önce kimliği değiştirebilirsiniz,
// ama şimdi buna ihtiyacımız yok
return $identity;
}
public function wakeupIdentity(IIdentity $identity): ?IIdentity
{
// kimlikteki rollerin güncellenmesi
$userId = $identity->getId();
$identity->setRoles($this->facade->getUserRoles($userId));
return $identity;
}
Ve şimdi çerez tabanlı depolamaya geri dönüyoruz. Oturum kullanmaya gerek kalmadan kullanıcıların giriş yapabileceği
bir web sitesi oluşturmanızı sağlar. Yani diske yazmaya ihtiyaç duymaz. Sonuçta, forum da dahil olmak üzere şu anda
okumakta olduğunuz web sitesi bu şekilde çalışmaktadır. Bu durumda, IdentityHandler
uygulaması bir
gerekliliktir. Sadece oturum açan kullanıcıyı temsil eden rastgele bir belirteci çerezde saklayacağız.
Bu yüzden önce security › authentication › storage: cookie
adresini kullanarak yapılandırmada istenen
depolama alanını ayarlıyoruz.
Veritabanına, her kullanıcının yeterli uzunlukta (en az 13 karakter) tamamen rastgele, benzersiz ve tahmin edilemez bir dizeye sahip olacağı bir
authtoken
sütunu ekleyeceğiz. CookieStorage
deposu çerezde yalnızca
$identity->getId()
değerini saklar, bu nedenle sleepIdentity()
'de orijinal kimliği kimlikte
authtoken
olan bir proxy ile değiştiririz, aksine wakeupIdentity()
yönteminde authtoken'a göre
veritabanından tüm kimliği geri yükleriz:
final class Authenticator implements
Nette\Security\Authenticator, Nette\Security\IdentityHandler
{
public function authenticate(string $username, string $password): SimpleIdentity
{
$row = $this->db->fetch('SELECT * FROM user WHERE username = ?', $username);
// şifreyi kontrol et
...
// veritabanındaki tüm verilerle birlikte kimliği döndürüyoruz
return new SimpleIdentity($row->id, null, (array) $row);
}
public function sleepIdentity(IIdentity $identity): SimpleIdentity
{
// ID'nin authtoken olduğu bir proxy kimliği döndürüyoruz
return new SimpleIdentity($identity->authtoken);
}
public function wakeupIdentity(IIdentity $identity): ?SimpleIdentity
{
// proxy kimliğini authenticate() işlevinde olduğu gibi tam kimlikle değiştirin
$row = $this->db->fetch('SELECT * FROM user WHERE authtoken = ?', $identity->getId());
return $row
? new SimpleIdentity($row->id, null, (array) $row)
: null;
}
}
Çoklu Bağımsız Kimlik Doğrulama
Bir site içinde birden fazla bağımsız oturum açmış kullanıcıya ve aynı anda bir oturuma sahip olmak mümkündür. Örneğin, ön uç ve arka uç için ayrı kimlik doğrulamasına sahip olmak istiyorsak, her biri için benzersiz bir oturum ad alanı ayarlayacağız:
$user->getStorage()->setNamespace('backend');
Bunun aynı segmente ait tüm yerlerde ayarlanması gerektiğini akılda tutmak gerekir. Sunucuları kullanırken, isim alanını ortak atada ayarlayacağız – genellikle BasePresenter. Bunu yapmak için checkRequirements() yöntemini genişleteceğiz:
public function checkRequirements($element): void
{
$this->getUser()->getStorage()->setNamespace('backend');
parent::checkRequirements($element);
}
Çoklu Kimlik Doğrulayıcılar
Bir uygulamayı bağımsız kimlik doğrulama ile segmentlere bölmek genellikle farklı kimlik doğrulayıcılar gerektirir.
Ancak, Authenticator uygulayan iki sınıfı yapılandırma hizmetlerine kaydetmek bir hatayı tetikleyecektir çünkü Nette
hangisinin Nette\Security\User
nesnesine otomatik bağlanması gerektiğini bilemeyecektir. Bu nedenle,
autowired: self
ile otomatik bağlamayı sınırlamalıyız, böylece yalnızca sınıfları özellikle istendiğinde
etkinleştirilir:
services:
-
create: FrontAuthenticator
autowired: self
class SignPresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private FrontAuthenticator $authenticator,
) {
}
}
Yalnızca login() yöntemini çağırmadan önce kimlik doğrulayıcımızı User nesnesine ayarlamamız gerekir, bu da genellikle oturum açma formu geri aramasında anlamına gelir:
$form->onSuccess[] = function (Form $form, \stdClass $data) {
$user = $this->getUser();
$user->setAuthenticator($this->authenticator);
$user->login($data->username, $data->password);
// ...
};