Kimlik Doğrulama Modülü Yazma
Kimlik Doğrulama modülü bir ikinci faktör yöntemidir. Giriş çekirdeğinin kullanıcıyı kaydetmek, soru sormak ve cevabı doğrulamak için çağırdığı metotları uygular.
Genel Bakış
Ürünle üç modül gelir: Email, Sms ve Totp. Taban sınıf yoktur. Çekirdek yöntem adından bir sınıf çözer ve her metodu method_exists ile yoklar.
İki aile vardır. Kayıt, kullanıcıya gösterilen bir sır üretiyor mu? Evet ise setup() uygularsınız. Hayır ise kodu iletmek için verification() uygularsınız.
Yönetici girişi ile müşteri girişi aynı çekirdekten geçer, yani tek modül ikisine de hizmet eder.
Ön Koşullar
coremio/modules/Authentication/altına yazma erişimi.- Bir iletim kanalı ya da paylaşılan sır şeması.
- Kod yöntemi için çalışan bir mail ya da SMS sürücüsü.
- Bildirim Şablonları Nasıl Çalışır.
Yapı
Üç dosya yeter. Ayar sayfası ve controller yoktur: operatör yalnızca yöntemi açar.
coremio/modules/Authentication/Acme/
├── Acme.php namespace WISECP\modules\Authentication; class Acme
├── config.php künye + ayarlar (setup, method, attempts, attempts_penalty_minute)
└── lang/
├── en.php name + description, yöntem kartında görünür
└── tr.php
Sınıf ad alanlıdır. Hazır modüller namespace WISECP\modules\Authentication; yazar, çekirdek ise WISECP\Modules\Authentication\{Yöntem} kurar. İkisi de çözülür.
Adım Adım
Modül Sınıfını Kurma
coremio/modules/Authentication/Acme/Acme.phpdosyasını dizin adını taşıyan bir sınıfla oluşturun.- Yapıcıda
config.phpdosyasını yükleyin. Yapıcı argüman almaz. - Global sınıfların başına ters bölü koyun:
\Session,\Filter,\Notification. - Ailenizin kümesini uygulayın:
setup()ileinstall(), ya daverification(). - İki durumda da
verify()uygulayın.
Kayıt
setup(array $user_data)sırrı ve sihirbazın göstereceği her şeyi döndürür. Düz kullanıcı dizisini alır.- Çekirdek kurulum verisini bir kez üretip oturumda saklar; sihirbaz ve etkinleştirme aynı kopyayı kullanır.
install(array $data)yazılan kodu saklanan veriye karşı denetler. Hata durumu kaydı iptal eder.setup()dönüşü tercihindataanahtarında şifreli saklanır, sonraki çağrılarda geri verilir.- Kapatma yolu için
uninstall(array $data)uygulayın.
Giriş Sorusu
verification(array $params)soru ekranında çağrılır. Kodu üretin, iletin, saklayın ve ekran yükünü döndürün.- İletim hız sınırını kendiniz koyun ve kalan saniyeyi
retry_delayile bildirin. verify(array $params, $code)gönderilen kodu alır. Başarı ya da hata durumunu döndürün; kilidi çekirdek uygular.- Kurtarma anahtarı için
verify_recovery(array $params, $key)ekleyin ve yükterecoverydeğerini true yapın. - İki ekranı da test edin: ayrı oturum ve ayrı sayaç tutarlar.
Referans
Metot Sözleşmesi
Her çağrı korunduğu için metotların hepsi isteğe bağlıdır. Bileşim değildir: ne setup() ne verification() taşıyan bir yöntem kaydedilemez.
| Metot | Ne zaman çağrılır | Aile |
|---|---|---|
setup(array $user_data): array | Hesap paneli, etkin yöntem yokken | sır tabanlı |
install(array $data = []): array | Kayıt kodu gönderildiğinde | sır tabanlı |
uninstall(array $data = []): array | Yöntem kapatıldığında | ikisi de |
verification(array $params = []): array | Soru ekranı, ya da hassas değişiklikten önce | kod tabanlı |
verify(array $params = [], $code = ''): array | Kod gönderildiğinde (giriş ya da yükseltme) | ikisi de |
verify_recovery(array $params = [], $key = ''): array | Kurtarma anahtarı gönderildiğinde | isteğe bağlı |
setup() varlığı dört ayrı yerde aile işareti olarak okunur. Panelin sihirbaz göstermesine ve hassas bir değişiklikten önce kod iletilmesine o karar verir. settings.setup config anahtarı aynı şeyi söyler ve önce o okunur.
İmzalar ve Yük Biçimleri
public function __construct();
// BİÇİM TUZAĞI: $user_data düz kullanıcı satırıdır (id, email, full_name),
// verify metotlarının aldığı ['user' => ..., 'data' => ...] sarmalayıcısı DEĞİL.
public function setup(array $user_data): array;
// $data tam olarak setup() metodunun döndürdüğüdür, oturum deposundan yeniden verilir.
public function install(array $data = []): array;
// $data saklanan tercih verisidir, yani setup() metodunun döndürdüğü.
public function uninstall(array $data = []): array;
// SIRA: önce params, sonra gönderilen değer.
// $params = ['user' => ['id' => 5, 'email' => '...'], 'data' => [ /* setup() ne döndürdüyse */ ]]
public function verification(array $params = []): array;
public function verify(array $params = [], string|int|null $code = ''): array;
public function verify_recovery(array $params = [], string|int|null $key = ''): array;
// setup(): kayıt sihirbazının bastığı her şey, artı saklanacak sır.
// _preview anahtarları, şablonun biçimi bilmek zorunda kalmadan ekranın
// karakterleri gruplayabilmesi için vardır.
return [
'secret_key' => 'JBSWY3DPEHPK3PXP',
'recovery_key' => 'K7Q2M9XR4TZB6WVA',
'secret_key_preview' => 'JBSW Y3DP EHPK 3PXP',
'recovery_key_preview' => 'K7Q2 M9XR 4TZB 6WVA',
'qr_code' => 'data:image/png;base64,iVBORw0KG', // bir data URI'si, yol değil
'content' => null, // sihirbaz için ek HTML, ya da null
];
// verification(): soru ekranının nasıl davranacağı.
return [
'digit' => 6, // kaç girdi kutusu çizileceği
'retry_delay' => 118, // yeniden göndermeye izin verilene dek saniye; 0 hemen demektir
'recovery' => true, // kurtarma anahtarı alanını sun (gizlemek için yazmayın ya da false)
'content' => null, // girdinin üstünde ek HTML, ya da null
];
// İletim hatası 'error' üzerinden bildirilir, çekirdek onu birebir yüzeye çıkarır.
return ['error' => 'Email could not be sent'];
// install(), uninstall(), verify(), verify_recovery(): bir durum, isteğe bağlı bir mesaj.
return ['status' => 'successful'];
return ['status' => 'error', 'message' => 'That code did not match.'];
Başarı sayılan tek şey birebir successful dizesidir. Dizi olmayan bir dönüş dahil diğer her şey bir deneme yakar.
Config Anahtarları ve Kayıt
config.php aile işaretini ve kilit politikasını taşır. Çekirdek bu bloğu modül üstverisinden okur.
setup() varlığıdır.
email, sms, totp).
['lang' => [...], 'config' => [...]]. Örnek, çözülen sınıf adı üzerinde new ile kurulur.
Kayıt Nerede Saklanır
Kullanıcı başına tek yöntem, kullanıcının bilgi kaydında şifreli bir blok olarak durur. Siz çözülmüş data yarısını alırsınız.
// install() kodu onayladıktan sonra enableTwoFactor tarafından yazılır.
$store = ['method' => $method];
if (method_exists($module, 'setup')) $store['data'] = $setupData;
User::setInfo($userId, ['authentication' => Crypt::encode(Utility::jencode($store), Config::get('crypt/user'))]);
// Her soruda geri okunur. Operatörün sonradan kapattığı bir yöntem burada
// false döner, böylece giriş ikinci faktör olmadan sürer.
$raw = User::getInfo($userId, ['authentication'])['authentication'] ?? '';
$pref = Utility::jdecode(Crypt::decode($raw, Config::get('crypt/user')), true);
// Ve verify() metodunuzun aldığı sarmalayıcı budur.
$params = ['user' => $userRow, 'data' => $pref['data'] ?? []];
Bunun etrafında iki kanca çalışır. gate:user.two_factor_disable bir mesaj döndürerek kapatmayı veto eder; action:user.two_factor_changed her açma ve kapamadan haberdar edilir.
Örnek
Bildirim sistemiyle ileten kod tabanlı bir yöntem.
<?php
namespace WISECP\modules\Authentication;
class Acme
{
public array $config;
private const DELAY = 120;
public function __construct()
{
$this->config = include __DIR__ . DS . 'config.php';
}
public function verification(array $params = []): array
{
$userId = (int) ($params['user']['id'] ?? 0);
$stash = $this->stash();
$remaining = self::DELAY;
$mayResend = true;
// Engellenen kullanıcılar ceza süresini beklerken iletim hakkı yakamamalıdır.
if (\User::CheckBlocked("member-login-authentication-attempt", $userId)) $mayResend = false;
if ($stash) {
$remaining = (int) $stash['expire'] - time();
if ($remaining > 0) $mayResend = false;
}
if ($mayResend) {
$expire = \DateManager::next_date(['second' => self::DELAY]);
$code = random_int(100000, 999999);
$sent = \Notification::dispatch('user', 'two-factor-verification', [
'user_id' => $userId,
'code' => $code,
'_sync' => true,
]);
// 'error' iletim hatası kanalıdır; çekirdek bu mesajı olduğu gibi basar.
if (!$sent) return ['error' => 'Verification code could not be delivered.'];
$this->stash(['code' => $code, 'expire' => \DateManager::strtotime($expire)]);
$remaining = self::DELAY;
}
return [
'digit' => (int) ($this->config['settings']['digits'] ?? 6),
'retry_delay' => max(0, $remaining),
'content' => null,
];
}
public function verify(array $params = [], string|int|null $code = ''): array
{
$stash = $this->stash();
if (empty($code) || !$stash || (string) $stash['code'] !== (string) $code)
return ['status' => 'error'];
// Tek kullanım: aynı kodun tekrarı geçemesin diye temizle.
\Session::delete('AcmeAuthData');
return ['status' => 'successful'];
}
public function uninstall(array $data = []): array
{
\Session::delete('AcmeAuthData');
return ['status' => 'successful'];
}
private function stash(?array $write = null): array
{
if ($write !== null) {
\Session::set('AcmeAuthData', \Utility::jencode($write), true);
return $write;
}
$raw = \Utility::jdecode((string) \Session::get('AcmeAuthData', true), true);
if (!$raw) return [];
// Süresi dolmuş depo yok demektir, yoksa bayat bir kod sonsuza dek geçerli kalır.
if ((int) ($raw['expire'] ?? 0) < time()) {
\Session::delete('AcmeAuthData');
return [];
}
return $raw;
}
}
// Örnek: önce Load, sonra çözülen sınıf adı üzerinde çıplak bir new.
$class = 'WISECP\\Modules\\Authentication\\' . $method;
$module = Modules::Load('Authentication', $method) && class_exists($class) ? new $class() : false;
$params = ['user' => $state['user'] ?? [], 'data' => $state['authentication']['data'] ?? []];
$recoveryKey = trim(str_replace(' ', '', $recoveryKey));
if ($recoveryKey !== '')
$verify = method_exists($module, 'verify_recovery') ? $module->verify_recovery($params, $recoveryKey) : ['status' => 'error'];
else
$verify = method_exists($module, 'verify') ? $module->verify($params, $code) : ['status' => 'error'];
if (!is_array($verify) || ($verify['status'] ?? 'error') !== 'successful') {
// Load burada ÜSTVERİ döndürür, deneme politikası oradan gelir.
$meta = Modules::Load('Authentication', $method) ?: [];
$total = (int) ($meta['config']['settings']['attempts'] ?? 0);
if ($total) {
$penalty = (int) ($meta['config']['settings']['attempts_penalty_minute'] ?? 5);
$attempts = (int) ($state['attempts'] ?? 0) + 1;
if ($total - $attempts < 1) User::addBlocked($reason, $userId, [], DateManager::next_date(['minute' => $penalty]));
else $state['attempts'] = $attempts;
}
return ['status' => 'error', 'message' => $message];
}
Tuzaklar
setup() düz kullanıcı dizisi alır ve $user_data['email'] okur. Diğerlerine ['user' => [...], 'data' => [...]] verilir. Yanlış biçim hata değil boş değer üretir.
Başarılı bir verify_recovery() çağrısında çekirdek kayıtlı tercihi siler, kullanıcı yeniden kaydolur. Anahtar bir kez kullanılır; kalıcı bir alternatif parola değildir.
Çekirdek yanlış cevapları sayar, yeniden göndermeleri değil. Yeniden yüklenen bir soru ekranı verification() metodunu tekrar çağırır, yani koşulsuz gönderim gelen kutusu doldurur. Bekleyen kodu saklayın ve retry_delay döndürün.
Çekirdek yakalamak yerine bir durum okur. Yanlış kodu ['status' => 'error'], iletim hatasını ['error' => '...'] olarak bildirin. Yakalanmamış bir fırlatma giriş ekranını bozar.
Kayıtlı tercih her okumada etkin listeye karşı denetlenir. Yönteminizi kapatmak kayıtlı kullanıcıları dışarıda bırakmaz; artık sorguya çekilmezler. Yeniden açınca soru geri gelir.
İlgili Makaleler
Geri bildiriminiz için teşekkürler!
Yukarıda bulamadığınız her şey için destek ekibimiz her zaman yanınızda.