Itt jársz most: Kezdőlap > Alkalmazásfejlesztés > Karakterkódolási probléma Spring keretrendszerben

Szűrő megjelenítése

Karakterkódolási probléma Spring keretrendszerben

Filterek

A filterek a Java Enterprise Edition (Java EE) részét képező eszközök, melyek a HTTP kérések és válaszok manipulálását teszik lehetővé. Ilyen filterekre épül többek között a karakterkódolás és a biztonsági modul (Spring Security) működése is. Ezen filterek a servletbe injektálva lépnek működésbe automatikusan, mikor egy kérés érkezik a szerver felé vagy az egy választ állít elő. A filterek egy láncba rendeződve, egymást követve futnak le, és ez fontos lesz a cikkem által vázolt probléma megoldása során. Egy alapvető szabályt ugyanis be kell tartani amennyiben felül szeretnénk bírálni az alapértelmezett (általában ISO-8859-2, vagy röviden latin2) karakterkódolást. A karakterkódolást felülbíráló filternek a láncban elsőként kell aktiválódni, a kérésben résztvevő adatok csak ezután futtathatók át a többi filteren. Ellenkező esetben a kliens felől érkező kérésben már rögzített lesz a karakterkódolás, a filter aktiválódása az adatfolyam újrakódolását eredményezi, amivel nyilvánvalóan nem a kívánt eredményt érjük el. A filterek sorrendje nagyon könnyen határozható meg: a konfigurációban meghatározott sorrendben kerülnek a láncba.

Konfigurációs megközelítések

A Spring többféle konfigurációs módszert támogat. Ez fontos tény abból a szempontból, hogy a „klasszikus” XML alapú konfiguráció esetén ez a hiba kisebb eséllyel fordul elő – akkor a fentebb ismertetett szabályt követve kikerülhető ez a hiba. XML alapú konfiguráció esetén a filterek a WEB-INF mappa gyökerében elhelyezendő web.xml állományban konfigurálhatók. A web.xml a Java EE technológiára épülő, webes alkalmazások inicializálási leíró állománya („Deployment Descriptor”). Olyan információkat tartalmaz az alkalmazásról, melyre a szervernek van szüksége az alkalmazás indításakor: például a servlet(ek)et és az(ok) belépési pontját, erőforrás útvonalakat, különféle inicializáló paramétereket, Spring esetén az alkalmazás kontextus konfigurációs fájljának helyét és persze – ahogy említettem – a filtereket is.

Amennyiben a karakterkódoláson kívül nincs más filterre szükségünk, egyszerű a dolgunk. A web.xml állományban megadjuk a szükséges paramétereket és készen is vagyunk. A karakterkódolási problémákat így jó eséllyel már el is felejthetjük – ha mégsem sikerülne, cikkem utolsó bekezdése szolgálhat pár jó tanáccsal. Most a példa kedvéért egy további filtert, a Spring Security modul filterláncát szúrom még be a konfigurációba. Nagyon fontos, hogy ennek, és minden más filternek is, a karakterkódolást szabályzó filter után kell következnie. A konfiguráció tehát az alábbi módon néz ki jelenleg:

<!-- character encoding -->
<filter>
	<filter-name>CharacterEncodingFilter</filter-name>
	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
	<init-param>
		<param-name>encoding</param-name>
		<param-value>UTF-8</param-value>
	</init-param>
	<init-param>
		<param-name>forceEncoding</param-name>
		<param-value>true</param-value>
	</init-param>
</filter>
<filter-mapping>
	<filter-name>CharacterEncodingFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>
 
<!-- Spring Security filterchain -->
<filter>
	<filter-name>springSecurityFilterChain</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
 
<filter-mapping>
	<filter-name>springSecurityFilterChain</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

Vegyük sorra, hogyan épül fel és mit csinál a fenti (standardnak tekinthető) konfiguráció. Minden filter beállítása két lépésből áll: a filter létrehozása (filter csomópont) és a filter hatáskörének beállítása (filter-mapping csomópont). A filter szekcióban kötelező paraméter a filter (tetszőleges) neve, az implementáló osztály minősített neve, továbbá opcionálisak az inicializálási paraméterek. A karakterkódolási filter esetén nyilván szükség lesz ilyenekre, mivel az „encoding” inicializálási paraméter segítségével változtatható meg a karakterkódolás (esetünkben UTF-8-ra). A „forceEncoding” paraméter segítségével minden esetben kényszeríthető a kódolás. A filter-mapping szekcióban megadott minta alapján dönti el a servlet, hogy az adott kérésre alkalmazni kell-e a kérdéses filtert. A példában megadott „/*” minta miatt minden kérésre érvényes lesz mindkét filterünk.

Java konfiguráció

Ha mindent jól csináltunk, nem szabad az alkalmazásban karakterkódolási problémáknak jelentkezni. Spring alkalmazás esetén azonban felmerülhet az igény, hogy az XML alapú konfigurációt teljesen Java alapúra cseréljük (vagy már eleve abban írjuk meg). Természetesen erre van lehetőségünk, a web.xml állomány is teljesen elhagyható. Mivel azonban ez a szervernek szóló információs fájl, nyilvánvalón más eszközre van szükség helyette: erre szolgálnak az inicializáló osztályok.

A web.xml állomány helyettesítéséhez a WebApplicationInitializer interfész implementálására van szükség. Ennek egyetlen metódusa van, onStartup() néven. Paraméterül egy ServletContext objektumot kap a szervertől (a szerver automatikusan találja meg az initializereket). A metódus törzsében a servlet kontextus számára kell biztosítanunk ugyanazokat a paramétereket, melyeket a web.xml-ben biztosítanánk, köztük a filtereket is. A karakterkódolás filtere az alábbi módon írható át Java alapú konfigurációra:

FilterRegistration characterEncodingFilter = servletContext
	.addFilter("CharacterEncodingFilter", CharacterEncodingFilter.class);
characterEncodingFilter.setInitParameter("encoding", "UTF-8");
characterEncodingFilter.setInitParameter("forceEncoding", "true");
characterEncodingFilter.addMappingForUrlPatterns(null, false, "/*");

Ez a kód gyakorlatilag pontosan ugyanazt csinálja, mint korábban a web.xml állomány. Az addFilter() metódus a servlet kontextusához adja a karakterkódolási filtert, paraméterül a filter nevét és implementáló osztályát várja. A setInitParameter() hívások az inicializáló paramétereket állítják be, végül az addMappingForUrlPatterns() hívással a hatáskör-minta adható meg. Ezután már csak a Spring Security filterének beállítása van hátra, azonban belefuthatunk a standard megoldások között egy jelen esetben éppen nem használhatóba is.

A standard megoldás szerint ugyanis a Spring Security egy eltérő inicializálóval indítható, mely automatikusan létrehozza a saját filterét. Kényelmes megoldás, csak az „AbstractSecurityWebApplicationInitializer” osztályt kell kiterjesztenünk, törzsét üresen hagyva – a megoldás részletes leírása megtalálható a Spring dokumentációjában is. Egy probléma van csupán ezzel: mi biztosítja, hogy az automatikusan létrejövő Security filterlánc valóban a karakterkódolási filter előtt indul el? A válasz az, hogy semmi. Ennek köszönhetően nem determinisztikus, hogy a servlet kontextusba melyik filterek kerülnek be előbb, lehet, hogy az adott indítás során a karakterkódolás az első, a következő alkalommal viszont az utolsó helyen fog szerepelni. Ez a misztikus programozási hibák körébe sorolható „egyszer jó – egyszer nem” jellegű hibát fog eredményezni, nem kevés fejfájás és hajtépés kíséretében – pedig a megoldás jóval nyilvánvalóbb, mint azt elsőre gondolnánk.

XML alapú konfiguráció esetén láthattuk, hogy a két filtert egymás után adtuk meg. Bár a dokumentáció egy jóval kényelmesebb megoldást kínál ennél számunkra, sajnos amennyiben felül akarjuk bírálni az alapértelmezett karakterkódolást, a némiképp bonyolultabb módszert kell alkalmaznunk. A Security inicializáló ugyanis valóban nem csinál mást, csupán hozzáadja a szükséges filtereket a servlet kontextusához. A megoldás egyértelmű: hozzuk létre manuálisan a filterláncot. Bővítenünk kell tehát az inicializáló onStartup() metódusát, méghozzá – nagyon fontos – tartva a sorrend szabályt. A karakterkódolási filter után tehát beszúrjuk az alábbi kódot:

FilterRegistration securityFilterChain = servletContext
	.addFilter("springSecurityFilterChain", new DelegatingFilterProxy("springSecurityFilterChain"));
securityFilterChain.addMappingForUrlPatterns(null, false, "/*");

Megint látható, hogy ugyanazokat a lépéseket adjuk meg, mint a web.xml állományban. A hatás azonban a Security inicializálóval ellentétben determinisztikus lesz: a sorrend mindig kötött marad. Természetesen így az „AbstractSecurityWebApplicationInitializer” osztályra nincs szükségünk.

Általános irányelvek

Természetesen számos további oka is lehet annak, hogy a karakterkódolás hibázik az alkalmazásban. A problémát okozhatja hibás alkalmazás- és/vagy szerverkonfiguráció, rosszul meghatározott karakterkódolás az alkalmazás bármely rétegében, de akár maga az input adathalmaz is. A legfontosabb szempont a karakterkódolási problémák elkerülésében a konzisztencia: lehetőleg próbáljuk az adatfolyam teljes útja során ugyanazt a kódolást használni, vagy ha valamiért ez nem lehetséges, ügyeljünk a megfelelő pontokban végrehajtott konverziókra. Az adatfolyam megfelelő kódolását jelentősen befolyásolják a következő „állomások”:

  1. Fájlok belső karakterkódolása

Tekintsük 0. állomásnak, mivel akkor van csak igazán jelentősége, ha beégetett szövegek vannak az állományban, de érdemes nem bízni a véletlenre, a forrásfájlok is jobb, ha UTF-8-ba vannak kódolva.

  1. A front alkalmazás karakterkódolása

Webes alkalmazás esetén a „charset” meta elhelyezése a head-ben. JSP használata esetén a @page direktívával szintén szabályozható a karakterkódolás is, ezt is érdemes használni, mivel a szerver ez alapján fogja tudni, hogyan kell kezelni a kéréseket és válaszokat.

  1. Alkalmazás belső karakterkódolása

Bizonyos esetekben az alkalmazáson belül is szabályozható a karakterkódolás (például fájlokkal dolgozó alkalmazás esetén az input és output állományok kódolása külön szabályozható).

  1. Perzisztens tároló karakterkódolása

XML, CSV, … tárolófájlok esetén a fájlok megfelelő karakterkódolása nagyon fontos. Adatbázis alapú tárolásnál ennél kicsit bonyolultabb a helyzet, mivel ott akár négy ponton is bukhat a karakterkódolás:

  • Az adatbázisszerver egyeztetése;
  • az adatbázis séma egyeztetése;
  • az adott séma tábláinak egyeztetése;
  • és a táblákban levő mezők karakterkódolása

összehangolt kell legyen, különben jó eséllyel belefutunk egy karakterkódolási problémába.

  1. Szerveralkalmazás vagy futtatókörnyezet karakterkódolása

Sajnos az is benne lehet a pakliban, hogy maga az alkalmazást futtató környezet (például a Tomcat, Glassfish, stb.) belső karakterkódolása rossz. Így tehát érdemes megvizsgálni annak a konfigurációját is.

Kommentek

Komment írásához jelentkezz be
Bejelentkezés

Még senki nem szólt hozzá ehhez a bejegyzéshez.