User se odhlašuje i když by neměl
- Ondřej Mirtes
- Člen | 1536
Aneb jak někdo pozná, že opět vyvíjím nějaký web – stoupne frekvence mého přispívání na fórum :o)
Dělám přihlašování uživatelů. Navěsil jsem si na onAuthenticated a onSignedOut události, které mi u daného usera nastavují online flag na 1/0 (abych mohl vypisovat uživatele, kteří jsou právě online apod.). Pochvaloval jsem si, že Nette dokáže ten callback zavolat i v případě, že nastane odhlášení kvůli zavření browseru. Geniální :)
Jenže to z nějakého neznámého důvodu přestalo fungovat – ta metoda se nezavolá (pokud se přihlásím, zavřu browser a otevřu ho, tak flag v databázi je stále na 1, ale já jsem odhlášený) a dokonce mě to odhlašuje i tehdy, kdyby nemělo. Zkouším totiž kromě mého typického nastavení třeba i něco takového:
$user->setExpiration('+ 2 days');
Což by podle dokumentace mělo znamenat, že maximální doba inaktivity před odhlášením je 2 dny, nehledě na zavření browseru. Jenže mě to po zavření/otevření odhlásí (koukal jsem na tu Nette cookie a má fakt platnost relace do zavření browseru) a navíc se nezavolá ta událost.
Nějaké tipy, kde bych měl hledat příčinu nesprávného chování, případně které zdrojáky bych sem měl postnout? Díky moc.
Editoval LastHunter (24. 6. 2009 10:56)
- Ondřej Mirtes
- Člen | 1536
R2D2 napsal(a):
a nemáš nastavenou menší expiraci session? (do zavření)
někde (v bootstrapu?) bys měl mít cosi jako
<?php $session = Environment::getSession(); $session->setExpiration($expiration); $session->start(); ?>
Díky, už funguje jak má :) Co kdyby třída User vyhazovala výjimku, pokud není splněná tato podmínka? Chová se pak totiž dost nestandardně…
- lactarius
- Člen | 47
Ondřej Mirtes napsal(a):
Dělám přihlašování uživatelů. Navěsil jsem si na onAuthenticated a onSignedOut události, které mi u daného usera nastavují online flag na 1/0 (abych mohl vypisovat uživatele, kteří jsou právě online apod.). Pochvaloval jsem si, že Nette dokáže ten callback zavolat i v případě, že nastane odhlášení kvůli zavření browseru. Geniální :)
Ahoj,
s online flagem v databázi je to dobrý nápad, akorát nevím, jak s událostmi onAuthenticated / onSignedOut – zkoušel jsem je nastavit v bootstrapu:
Environment::getUser()->onAuthenticated[] = 'UsersModel::userAuthenticated';
Environment::getUser()->onSignedOut[] = 'UsersModel::userSignedOut';
UsersModel:
public static function userAuthenticated()
{
$name = Environment::getUser()->getIdentity()->username;
dibi::query("UPDATE users SET online=TRUE WHERE username='$name'");
}
public static function userSignedOut()
{
$name = Environment::getUser()->getIdentity()->username;
dibi::query("UPDATE users SET online=FALSE WHERE username='$name'");
}
Takhle to nějak funguje – ale nejsem si jist, co právě v připadě ‚neregulérního‘ odhlášení – tj. vypnutí prohlížeče – pokud bude expirace nastavena třeba na + 14 days, tak bude ta informace dost nepravdivá…
- Vyki
- Člen | 388
Taky jsem se na něčem podobném nachytal, ale naštěstí jsem měl u sebe poznámky a zdrojáky z Davidovo přednášky, které mi to pomohly vyřešit. Ono to dává logiku, že žádná samostatná session proměnná nemůže mít dobu expirace nastavenou na delší dobu než je nastaveno obecně pro session (všechny session).
Editoval Vyki (26. 1. 2010 16:08)
- lactarius
- Člen | 47
Ještě k události onSignedOut – dnes jsem měl možnost to v klidu otestovat. Tato událost skutečně proběhne pouze v případě, že se uživatel ‚legálně‘ odhlásí.
Environment::getUser()->signOut();
Jinak se změna stavu uživatele projeví až při přihlášení jiného
uživatele do stejného prostoru, něco podobného už se tu řešilo:
https://forum.nette.org/…thenticatory
Takže tudy cesta ke zjištění ‚fyzické přítomnosti‘ uživatele
nevede…
- Ondřej Mirtes
- Člen | 1536
Jo, na to jsem už taky přišel. Navíc by to hlásilo jako online lidi třeba ty, kteří na webu vůbec nejsou, ale mají aktivní „stálé přihlášení“ třeba na 14 dní.
Jako možné řešení se nabízí zaznamenávat „last click“
u každého uživatele a vybírat pak jen ty řádky, kde
"last_click > time() - 1800"
(kliky za poslední
půlhodinu).
Editoval Ondřej Mirtes (28. 1. 2010 18:01)
- lactarius
- Člen | 47
Takže místo online flagu u každého uživatele záznam času poslední
akce – třeba odeslání formuláře. Plus sekvenční kontrola všech
uživatelů – buď ručně, nebo v intervalu.
Asi nejsofistikovanější odpověď by přišla od někoho ze Spedie nebo
CashSurfers – ale tam by zřejmě nebyli moc sdílní…
- Ondřej Mirtes
- Člen | 1536
Stačí dát volání toho updatu přihlášeného uživatele třeba do startupu BasePresenteru.
dibi::query('UPDATE [users] set [last_click]=%i', time(), ' WHERE [id]=%i', $this->getUser()->getIdentity()->getId());
A počet aktuálně online (aktivních uživatelů za poslední půlhodinu):
$onlineCount = dibi::fetch('SELECT count(*) FROM [users] WHERE [last_click]>%i', time() - 1800);
Žádný cron nebo něco takového zde není potřeba. Je to jen základní princip, třeba někoho napadnou „zlepšováky“.
Editoval Ondřej Mirtes (28. 1. 2010 18:02)
- lactarius
- Člen | 47
Jo – tak takhle to fakt funguje. Testoval jsem to na chatu, takže jsem
update nedal do BasePresenteru, ale do handle, který se stará o zpracování
zpráv – přičemž se stav kontroluje ve chvíli, kdy odněkud dorazí
zpráva – sice by to šlo kontrolovat pokaždé, kdy si klient ‚sáhne‘
na server pro případné nové zprávy – ale naco, takhle to bohatě
stačí.
handleExchangeMsg:
$limitTime = $actualTime - $this->idleLimit;
$users = dibi::fetchAll("SELECT username FROM users WHERE action > $limitTime");
$this->payload->users = $users;
a u klienta se v panelu uživatelů prostě vypíšou pouze ti aktivní:
var users = payload.users;
var chatUsers = $('#chatUsers');
chatUsers.empty();
for (i in users) {
chatUsers.append('<li>'+users[i].username+'</li>');
}
- lactarius
- Člen | 47
A máš určitě nastavenou jak expiraci session
bootstrap:
$session = Environment::getSession();
$session->setExpiration('+ 14 days');
tak uživatele ?
BasePresenter (třeba):
if ($memo) {
$this->getUser()->setExpiration('+ 14 days', FALSE);
} else {
$this->getUser()->setExpiration('+ 20 minutes', TRUE);
}
- i.magine
- Člen | 81
Tak to jsem nemel… kazdopadne porad to nebezi:
public function formLoginSubmitted(AppForm $form)
{
try {
$session = Environment::getSession();
$session->setExpiration("+ 5 days");
$user = Environment::getUser();
$user->setAuthenticationHandler(new Users);
$user->setExpiration('+ 5 days', FALSE);
$user->authenticate($form['username']->getValue(), $form['password']->getValue());
$this->presenter->redirect('UserAdmin:default');
} catch (AuthenticationException $e) {
$form->addError($e->getMessage());
}
}
Když se podívám do cookies tak po opětovném zapnutí prohlížeče už je PHPSESSID pryč. A vždy se u něj nastavuje Doba platnosti na žádnou hodnotu.
Díky
Editoval i.magine (29. 1. 2010 12:06)
- Ondřej Mirtes
- Člen | 1536
Přenes to nastavení expirace session do bootstrapu, jak ti radí příspěvek nad tím.
- Honza Kuchař
- Člen | 1662
@R2D2: a nemáš nastavenou menší expiraci session?
Nebylo vy fajn, kdyby to vyhazovalo výjimku?
Editoval honzakuchar (29. 1. 2010 13:17)