V úvodu do PrimeFaces a JSF 2 jsem se zmínil, že používáte-li Spring Security, nepotřebujete managed beanu pro login, stačí jen submit javascriptem (POST request na url /j_spring_security_check). Mnohem zapeklitější je ošetření ajaxu nad vypršenou HTTP session. Nejspíš si říkáte, žádná věda. Spring Security mě prostě přesměruje na login. Taky že ano, ale v případě ajaxu dostanete jako partial response právě onen login formulář. View ho není schopné zpracovat a uživatel je zmaten, protože se nic neděje. Jak to vyřešit?

Klíčovým bodem je implementace rozhraní InvalidSessionStrategy. Začneme tím jednodušším, pro neajaxový request zavoláme HttpServletResponse#sendRedirect(String). A jak vůbec ajaxový request rozlišit? V HTTP hlavičce faces-request dostanete hodnotu partial/ajax. Pak už stačí vytvořit xml odpověď s direktivou pro přesměrování.

Zbývá vyřešit několik drobností. Především ve web descriptoru musí být zaregistrován HttpSessionEventPublisher, abyste vůbec dostávali eventy o vypršené session. Naši implementaci InvalidSessionStrategy injektujeme do filteru SessionManagementFilter, který zaregistrujeme na pozici

before="SESSION_MANAGEMENT_FILTER"

Problém je, že jako vedlejší efekt samotného Ajax POSTu, se InvalidSessionStrategy volá na GET requesty způsobené nějakými obrázky (přestože je to cache hit - HTTP status 304 Not Modified). Stačí neaplikovat zabezpečení na resources (css, javascript a obrázky).

<sec:http pattern="/javax.faces.resource/**" security="none"/>

Provedete-li GET request a nejste-li autentizovaní, jste přesměrováni na login. Po úspěšném přihlášení jste ovšem opět přesměrováni, tentokrát na stránku, kterou jste původně žádali. To je standardní chování. Naše implementace má však potíž se zacyklením. Sice jste přesměrováni ale se stále stejnou, vypršenou, session. Vytvoříme tedy novou.

request.getSession(true);

S ajaxovým POSTem je to složitější. Je potřeba uživatele informovat, že došlo k vypršení session s možností prokliku na view, kde byl naposled, ačkoliv původní kontext je ztracen. View dostaneme z referera: #{header[‘referer’]}

Zde je kompletní konfigurace.