V tomto článku sa zameriame na podpornú technológiu súvisiacu nie len so servletmi ale aj so stránkami JSP. Pôjde o systém filtrovania informácií resp. prichádzajúcich požiadaviek a následného vyhodnocovania týchto údajov. V závislosti na výsledku vyhodnocovania sa vykoná preddefinovaná činnosť. Servlet filtering poskytuje štandardizovanú alternatívu k neštandartnému systému tzv. „reťazenia servletov“ (servlet chaining).

Hneď na začiatok musím upozorniť, že filtre priniesla až verzia Java Servlets 2.3. Na serveroch nepodporujúcich túto verziu filtre nebudú funkčné. Filter je v podstate program bežiaci na strane servera, ktorý sa vykonáva ešte pred samotným servletom s ktorým je asociovaný. Pričom filter môže byť asociovaný s viacerými servletmi. Úlohou filtra je zväčša prečítať si informácie z objektu request a následne vykonať niektorú z týchto možností:

  1. Predať riadenie servletu úplne normálne bez zmeny.
  2. Predať riadenie servletu s modifikovanými informáciami objektu request.
  3. Predať riadenie servletu s modifikáciou objektu response.
  4. Presmerovať požiadavku na iný servlet ako ten asociovaný s filtrom, vrátiť niektorý status kód alebo vygenerovať vlastný výstup.

Pomocou filtrov môžete napríklad obmedziť prístup k vybraným stránkam pre vybraných uživateľov, z jedného miesta (filter) robiť zmeny v mnohých servletoch a pod. Skrátka môžete zapuzdriť určité chovanie do externej komponenty, ktorú je možné ľubovoľne využiť.

Vytvorenie filtra

Vytvorenie filtra si rozdelíme do piatich krokov:

  1. Vytvorenie triedy filtra implementujúcej javax.servlet.Filter – toto rozhranie obsahuje iba tri metódy. Metóda init() zabezpečuje inicializáciu filtra a ako vstupný parameter akceptuje objekt triedy FilterConfig. Servlet kontajner zavolá túto metódu iba raz; pri vytváraní inštancie filtra. Metóda doFilter() vykonáva samotné filtrovanie. Ako parametre akceptuje objekty tried ServletRequest, ServletResponse a FilterChain, všetky z balíčka javax.servlet. Posledná metóda je destroy(), ktorá má za úlohu „upratať“ všetky držané zdroje. Jej význam je podobný ako význam rovnomennej metódy v triede javax.servlet.Servlet.
  2. Vloženie logiky filtrovania do metódy doFilter() – ako som uviedol, prvým argumentom metódy doFilter() je objekt request. Môžeme z neho získať formulárové dáta, cookies, obsah hlavičky protokolu HTTP a pod. Druhý argument objekt response sa v prevažnej miere ignoruje, ale existujú dva prípady kedy by ste ho mohli využiť. Tretí argument je objekt FilterChain, ktorého použitie je vysvetlené v nasledujúcom kroku.
  3. Zavolanie metódy doFilter() objektu FilterChain – ako som už spomenul metóda doFilter() rozhrania Filter akceptuje FilterChain ako jeden z argumentov. Ak zavoláte metódu doFilter() tohto objektu bude spustený ďalší asociovaný filter. Ak žiadny ďalší filter nie je asociovaný so servletom prípadne s JSP, potom je proces predaný samotnému servletu. Nezabudnite, že úplne na začiatku tu bola požiadavka smerovaná na servlet (JSP). Ale prv ako by sa mu predalo riadenie vykoná sa filter. A filtrov môže byť pre jeden servlet viac.
  4. Priradenie filtra k servletu – na tento účel sa v deskriptore web.xml používajú dva elementy <filter> a <filter-mapping>.
  5. Zablokovanie priameho prístupu k servletu – možno to vyzerá nezmyselne ale treba si uvedomiť aj stránku zabezpečenia aplikácie. Ak užívateľ má možnosť obísť použitie filtra môže sa o to pokúsiť. Preto treba tejto možnosti zabrániť.

V bode 2. som uviedol, že existujú dva dôvody kedy by ste druhý argument metódy doFilter() objekt response mohli potrebovať. Prvý je ten keď potrebujete úplne zablokovať prístup k asociovanému servletu. Vtedy môžete využiť response.getWriter() a rovno poslať klientovi odpoveď. Druhý dôvod je prípad ak chcete modifikovať výstup vygenerovaný servletom. Na tento účel je však treba vytvoriť špeciálnu triedu zdedenú od HttpServletResponseWrapper.

Zaregistrovanie filtra

Spomínal som, že na tento účel je nutné zeditovať deskriptor web.xml. Hlavným elementom je <filter>. Musí byť umiestnený ešte pred akýmkoľvek <servlet>, <servlet-mapping>, alebo <filter-mapping> elementom. Obsahuje niekoľko subelementov pričom najdôležitejšie sú tieto tri:

  • <filter-name> – tento element je vyžadovaný a predstavuje meno pod ktorým bude filter zaregistrovaný
  • <filter-class> – je takisto povinný a obsahuje plne kvalifikovaný názov triedy filtra
  • <init-param> – toto je voliteľný element ktorý je možné prečítať metódou FilterConfig.getInitParameter(). Na rozdiel od dvoch predchádzajúcich sa môže tento element vyskytovať v jednom filtri viackrát.

<?xml version=“1.0″ encoding=“ISO-8859-1″?>
<!DOCTYPE web-app PUBLIC
   „-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN“
   „http://java.sun.com/dtd/web-app_2_3.dtd“>
<web-app>
  <filter>
    <filter-name>Filter</filter-name>
    <filter-class>package.FilterClass</filter-class>
  </filter>
</web-app>

Asociovanie filtra so servletom

Na vytvorenie dvojice filter-servlet použijeme element <filter-mapping>, ktorý musí byť umiestnený až za elementom <filter> ale pred elementom <servlet>. Obsahuje tieto tri subelementy:

  • <filter-name> – má rovnaký význam ako pri elemente filter. Musí obsahovať rovnaký názov filtra
  • <url-pattern> – deklaruje tvar URL začínajúci na (/) na ktorý sa má filter aplikovať
  • <servlet-name> – obsahuje názov servletu tak ako je zaregistrovaný v deskriptore web.xml, na ktorý sa má aplikovať filter

<?xml version=“1.0″ encoding=“ISO-8859-1″?>
<!DOCTYPE web-app PUBLIC
   „-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN“
   „http://java.sun.com/dtd/web-app_2_3.dtd“>
<web-app>
  <filter-mapping>
    <filter-name>Filter</filter-name>
    // buď použijete …
    <url-pattern>/aplication/MyServlet</url-pattern>
    // alebo …
    <servlet-name>MyServlet</servlet-name>
  </filter-mapping>
</web-app>

Zablokovanie priameho prístupu k servletu

V 5. bode som načrtol myšlienku, že existuje teoretická možnosť aby sa užívateľ pokúsil obísť použitie filtra. Inak povedané máme servlet zaregistrovaný a namapovaný na nejaké url. Na toto url alebo na meno servleta máme asociovaný filter.

<?xml version=“1.0″ encoding=“ISO-8859-1″?>
<!DOCTYPE web-app PUBLIC
   „-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN“
   „http://java.sun.com/dtd/web-app_2_3.dtd“>
<web-app>
  <filter>
    <filter-name>Filter</filter-name>
    <filter-class>package.FilterClass</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>Filter</filter-name>
    <servlet-name>MyServlet</servlet-name>
  </filter-mapping>
  <servlet>
    <servlet-name>MyServlet</servlet-name>
    <servlet-class>interval.MyServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/myservlet</url-pattern>
  </servlet-mapping>
</web-app>

Všetko bude OK ak klient požiada o takéto url http://host:port/webapp/myservlet. Vtedy sa na servlet MyServlet aplikuje filter. Avšak ak užívateľ zadá http://host:port/webapp/servlet/interval.MyServlet podarí sa mu použitie filtra obísť. Ako si s tým poradiť? Máme dve možnosti pričom obidve predpokladajú použitie špeciálneho „error“ servletu. Pri prvej zaregistrujeme tento error servlet pod týmto url pattern-om /servlet/* čo znamená, že všetky pokusy smerujúce na túto adresu budú presmerované na error servlet. Druhá možnosť je podobná s tým rozdielom, že url pattern sa bude vzťahovať na konkrétny servlet. Ak však potrebujete takto zabezpečiť viac servletov je nutné urobiť to pre každý zvlášť.

Príklad

TimeFilter.java: Treba si pozrieť hlavne metódy init() a doFilter(). Prostredníctvom prvej získam z deskriptoru web.xml dva inicializačné parametre: názov firmy a šablónu podľa ktorej sa má zobrazovať dátum a čas. Výhodou tohto prístupu je, že pri zmene firmy alebo dátumovej šablóny nie je nutné editovať zdrojový kód servletu a znova robiť deployment. Z toho vyplývajú menšie náklady na údržbu aplikácie. V metóde doFilter() získam objekt PrintWriter a priamo zapíšem dátum a čas na výstup objektu response. Ďalej vezmem parameter názvu firmy a vložím ho do objektu request ako atribút. Nie je možné priamo nastaviť alebo zmeniť parametre objektu request, je možné ich len prečítať. Teda neexistuje metóda typu setParameter().

TimeServlet.java: Po spracovaní filtra sa vykoná servlet. V ňom iba za predpokladu, že nie je null, prečítam atribút obsahujúci informáciu o firme a vložím ho do výstupu. Servlet ešte vypíše priamy odkaz na seba aby ste videli výsledok zablokovania priameho prístupu.

ErrorServlet.java: Je to len primitívny servlet ktorý vypíše upozornenie a poskytne správny odkaz. Na koniec ešte prikladám výpis z web.xml potrebný na správne fungovanie príkladu.

Príklad si môžete skúsiť tiež on-line alebo si ho stiahnuť ako zip. Neukázali sme si všetky možnosti ktoré filtre poskytujú, ale to ani nebolo náplňou tohto článku.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

1 Příspěvěk v diskuzi

Odpovědět