zamezení vícenásobného přihlášení jednoho uživatele
- bagr_001
- Člen | 3
Ahoj, v aplikaci řeším unikátní přihlašování uživatelů. Resp. aby se na jeden login mohl současně uživatel přihlásit jen jednou. Pokud je již přihlášen a přihlásí se podruhé, na prvním místě se odhlásí. vymyslel jsem následující řešení: Po přihlášení vygeneruji unikátní hash, který se uloží do DB a do session. Při každém refreshi znovu načtu identitu uživatele (vč. hashe) a zkontroluji s tou v session. Pokud se neshodují, uživatele to odhlásí. Myslíte že je mé řešení dobré, nebo by se to dalo udělat „elegantněji“?
Díky.
abstract class BasePresenter extends Nette\Application\UI\Presenter
{
protected $section;
protected $uzivatele;
protected function startup() {
parent::startup();
$this->section = $this->session->getSection('MySection');
$this->uzivatele = $this->context->uzivatele;
if($this->user->isLoggedIn()){
$this->reloadUserData();
if(!$this->isHashValid()){
$this->user->logout(true);
unset($this->section->loginHash);
$this->flashMessage('Byl jste odhlášen. Tento účet byl použit z jiného počítače!');
$this->redirect('Homepage:');
}
}
}
public function reloadUserData(){
$user_data = $this->uzivatele->findById($this->user->id);
unset($user_data->heslo);
$this->user->storage->setIdentity(new NS\Identity($user_data->id_uzivatele, NULL, $user_data->toArray()));
}
public function isHashValid(){
return $this->section->loginHash == $this->user->identity->data['loginhash'];
}
}
class SignPresenter extends BasePresenter
{
..
public function signInFormSubmitted(Form $form)
{
try {
$user = $this->getUser();
$values = $form->getValues();
if ($values->persistent) {
$user->setExpiration('+30 days', FALSE);
}
$user->login($values->email, $values->heslo);
$hash = Uzivatele::generateLoginHash();
$this->uzivatele->saveHash($hash, $this->user->id);
$this->section->loginHash = $hash;
$this->reloadUserData();
$this->flashMessage('Přihlášení bylo úspěšné.', 'success');
$this->redirect('Homepage:');
} catch (Nette\Security\AuthenticationException $e) {
$form->addError($e->getMessage());
}
}
public function actionOut()
{
$this->uzivatele->unsetHash($this->user->id);
unset($this->section->loginHash);
$this->getUser()->logout(true);
$this->flashMessage('Byl jste odhlášen.');
$this->redirect('in');
}
}
class Uzivatele extends \Nette\Object {
private $db;
public function __construct(\DibiConnection $connection)
{
$this->db = $connection;
}
public function findByEmail($email) {
return $this->db->query("SELECT * FROM `uzivatele` WHERE `email` = %s", $email)->fetch();
}
public function findById($id) {
return $this->db->query("SELECT * FROM `uzivatele` WHERE `id` = %i", $id)->fetch();
}
public function saveHash($hash, $uid){
$this->db->query("UPDATE `uzivatele` SET `loginhash` = %s WHERE `id` = %i", $hash, $uid);
}
public function unsetHash($uid){
$this->db->query("UPDATE `uzivatele` SET `loginhash` = NULL WHERE `id` = %i", $uid);
}
public static function generateLoginHash(){
return md5(uniqid() . microtime() . rand());
}
}
- David Matějka
- Moderator | 6445
princip s tim hashem je ok, provest by to slo lepe, par poznamek:
- udelej si vlastni identitu implementujuci Nette\Security\IIdentity, ktera bude do session ukladat id a ten hash (to urcis v metode __sleep()
- tu logiku presun z presenteru jinam – treba to UserStorage – inspirovat se muzes zde
v tom UserStorage by slo vyresit vsechno – znovu nacitani identity, overeni hashe a vraceni logout reasonu v pripade neplatneho hashe
- bagr_001
- Člen | 3
Moc děkuju za rady. Implementoval jsem upravené třídy User a UserStorage a funguje to skvěle. Jen mám problém s tím, že při každé žádosti o data uživatele se aktualizuje UserStorage. Takže pokaždé když použiju $this->user->data… tak si to sahá do DB. Což mi ve výsledku přijde až dost paranoidní, ověřovat platnost účtu 10× během vykreslení stránky. Nebylo by lepší to přesunout do třídy User a kontrolovat validitu jen při vytvoření a prvním načtení identity?