Java na webovém serveru: Komentáře a integrace s Texy
Možnost vkládat komentáře, resp. schopnost přijímat od uživatelů formátovaný text, to je vlastnost, bez které se neobejde skoro žádný web. V dnešním díle přidáme do naší aplikace podporu komentářů a ukážeme si, jak je kontrolovat, aby nám do nich potenciální útočník nemohl podstrčit žádná závadná data, která by narušila naši stránku. Kromě XHTML a prostého textu umožníme čtenářům psát komentáře ještě v jednom formátu – Texy.
Seriál Java na webovém serveru
- Java na webovém serveru: SOAP webové služby
- Java na webovém serveru: hlasování a grafy v SVG
- Java na webovém serveru: Komentáře a integrace s Texy
- Java na webovém serveru: AJAX formuláře
- Java na webovém serveru: implementujeme Jabber
Naše aplikace obsahuje (nebo spíš bude obsahovat) databázi hospod a jiných podobných podniků, a tak by bylo dobré, kdyby je návštěvníci mohli komentovat a napsat, jak se jim tam líbilo. Web je postavený na XHTML (nebo HTML) a tak se přímo nabízí tenhle formát použít i pro zadávání komentářů. Někteří uživatelé ho ale neovládají, a tak je dobré jim dát i jinou možnost – psaní v prostém textu (což bude většině lidí stačit) nebo Texy. Texy! je dílo Davida Grudla a slouží k snadnému zápisu formátovaného textu (i když mně osobně přijde přirozenější psát ostré závorky)
Přejdeme rovnou k věci a jako obvykle si stáhneme aktuální zdrojové kódy aplikace z Mercurialu:
$ hg pull $ hg up "14. díl"
Případně je můžete stáhnout jako bzip2 archiv přes web.
Proces zpracování komentáře
Při odesílání si uživatel může vybrat, v jakém formátu komentář posílá (v další verzi bychom mohli přidat automatickou detekci). Tím se nám proces rozděluje na tři větve – v prostém textu alespoň přidáme na konce řádků <br/>, aby se řádky neslily v jeden nekonečný nepřehledný tok textu (když už si uživatel dal práci a napsal několik odstavců). V případě Texy předáme vstupní text externí službě. A do XHTML pak doplníme odstavce, pokud je uživatel nezadal sám. Pak se cesty opět spojují a komentář odešleme k validaci. Ta je důležitá, aby nám na stránku někdo nevložil škodlivý kód (javascript injection) nebo třeba příliš velké písmo nebo neuzavřenou HTML značku, která by narušila naši stránku.
Napojení na Texy
Jelikož je knihovna Texy napsaná v PHP a my používáme Javu, musíme nějak vyřešit propojení těchto dvou světů. Existuje sice i port Texy do Javy, ale vzhledem k tomu, že chybí formální specifikace Texy, rozhodl jsem se použít originální PHP implementaci. Vytvořil jsem velice jednoduché „API“ – pomocí HTTP POST odešleme prostý text na URL http://nekurak.net/texy/http/ a jako odpověď dostaneme XHTML. Implementace rozhraní na straně Javy:
public class Texy {
/** TODO: parametrizovatelnost */
private static final String URL_SLUZBY = "http://nekurak.net/texy/http/";
private static final Logger log = Logger.getLogger(Texy.class.getSimpleName());
public String preved(String text) throws TexyVyjimka {
OutputStreamWriter wr = null;
BufferedReader rd = null;
try {
URL url = new URL(URL_SLUZBY);
URLConnection spojeni = url.openConnection();
spojeni.setDoOutput(true);
/** Odešleme data */
wr = new OutputStreamWriter(spojeni.getOutputStream());
wr.write(URLEncoder.encode(text, "UTF-8"));
wr.flush();
/** Přijmeme odpověď */
rd = new BufferedReader(new InputStreamReader(spojeni.getInputStream()));
StringBuffer vysledek = new StringBuffer();
String radka;
while ((radka = rd.readLine()) != null) {
vysledek.append(radka);
}
return vysledek.toString();
} catch (Exception e) {
throw new TexyVyjimka("Chyba při zpracovávání textu: " + text, e);
} finally {
try {
wr.close();
} catch (IOException e) {
log.log(Level.WARNING, "Selhalo zavírání OutputStreamWriteru", e);
}
try {
rd.close();
} catch (IOException e) {
log.log(Level.WARNING, "Selhalo zavírání BufferedReaderu", e);
}
}
}
}
Na straně serveru (PHP) je implementace tohoto rozhraní velice snadná, ale v Javě kvůli ní musíme psát víc kódu než je nutné (ošetřování chyb, řešení nízkoúrovňových operací). Texy funkcionalita je zapouzdřena ve třídě cz.frantovo.nekurak.ext.Texy, takže v budoucnu můžeme snadno přepsat rozhraní na SOAP webové služby či XML-RPC – nebo použít nativní implementaci Texy v Javě (port odkazovaný výše). Případně můžete v komentářích navrhnout, jak byste tuto úlohu řešili pomocí REST API, ale nemyslím si, že by to v tomto případě bylo vhodné.
XML schéma – XSD
Ať už uživatel zadal text pomocí Texy nebo jako prostý text, klíčová je nakonec validace výsledného XHTML.
Pro kontrolu povolených značek (a vůbec formátování) použijeme XML Schéma. Díky tomu nebudeme muset moc programovat a pouze deklarujeme, jaké značky případně atributy jsou povolené. Vytvořené schéma je velmi jednoduché – povoluje jen odstavce, tučné a skloněné písmo a ruční zalomení řádku. Další značky a atributy si můžete podle potřeby přidat do schématu v souboru komentář.xsd. Např. povolit odkazy nebo odrážky.
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="div">
<xs:complexType>
<xs:choice minOccurs="1" maxOccurs="unbounded">
<xs:element name="p">
<xs:complexType mixed="true">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="em" type="xs:string"/>
<xs:element name="strong" type="xs:string"/>
<xs:element name="br"/>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
Jelikož každé XML musí mít kořenový element, použili jsme <div/>. Mohli bychom si vymyslet i vlastní kořenovou značku a vůbec celý formát, ale podobnost s XHTML se nám hodí – uživatelé se nemusí učit novou syntaxi a my nemusíme dělat další konverzi při vypisování na stránku.
Podpora XSD v Netbeans
V Netbeans existuje pěkný plugin pro vizuální návrh XML schématu. Ve výchozí instalaci ho pravděpodobně nemáte dostupný, ale dá se to snadno napravit – stačí si přidat vývojářské úložiště zásuvných modulů – jeho URL je:
http://updates.netbeans.org/netbeans/updates/6.8/uc/m1/dev/catalog.xml.gz
a doinstalovat si „XML Schema and WSDL“.

Validace v Javě
Kód, který se stará o kontrolu XML oproti schématu se nachází v metodě zkontroluj() třídy Komentare (v modulu nekurak.net-lib).
public static Document zkontroluj(String komentar) throws KomentarovaVyjimka {
try {
URL soubor = Komentare.class.getClassLoader().getResource("cz/frantovo/nekurak/util/komentář.xsd");
SchemaFactory tovarnaSchemat = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = tovarnaSchemat.newSchema(soubor);
DocumentBuilderFactory tovarnaDB = DocumentBuilderFactory.newInstance();
tovarnaDB.setSchema(schema);
DocumentBuilder db = tovarnaDB.newDocumentBuilder();
db.setErrorHandler(new ErrorHandler() {
public void warning(SAXParseException e) throws SAXException {
throw e;
}
public void error(SAXParseException e) throws SAXException {
throw e;
}
public void fatalError(SAXParseException e) throws SAXException {
throw e;
}
});
Document dokument = db.parse(new ByteArrayInputStream(komentar.getBytes("UTF-8")));
return dokument;
} catch (Exception e) {
throw new KomentarovaVyjimka("Neplatný komentář: " + komentar, e);
}
}
Pomocí vlastního ErrorHandleru můžeme zjistit, v čem přesně dokument nevyhovuje a sdělit to uživali, aby věděl, co má opravit. V případě platného XML vrací metoda zkontroluj() DOM (který zatím k ničemu nevyužíváme) a v případě chyby vyhazuje výjimku – ta v sobě obsahuje informaci, které elementy jsou chybné, ale v ne příliš čitelné podobě.
Validace v Linuxu
Správnost XML dokumentů si můžeme kontrolovat i bez Javy a nějakých složitých IDE, stačí nám k tomu příkazová řádka a nástroj xmllint. V linuxových distribucích jako je Debian nebo Ubuntu si ho nainstalujeme pomocí:
$ aptitude install libxml2-utils
A kontrolu provádíme jednoduše příkazem:
xmllint --schema komentář.xsd komentář.xml
Pokud jsme nikde neudělali chybu, program nám odpoví výpisem XML souboru a hláškou komentář.xml validates. To se nám může hodit pro různé ladění nebo dávkové testování XML dokumentů na disku.
Validace při výstupu v JSP
Tak, jak jsme aplikaci vytvořili, teď závisí její bezpečnost na kvalitě dat v databázi. Pokud zajistíme, že se do ní bude zapisovat jen přes aplikaci (tzn. zkontrolované komentáře) nebo že do ní nikdo nezanese ručně chyby, můžeme aplikaci nechat tak, jak je. Pokud ale chceme vyšší bezpečnost, je lepší kontrolovat validitu komentářů i při jejich vypisování a nespoléhat se na kvalitu dat v databázi.
K tomu můžeme použít vlastní JSTL funkci nkfn:zkontrolujKomentar(). Pomocí tohoto kódu vypíšeme platné komentáře s formátováním a neplatné bez něj – escapované:
<c:out value="${k.komentar}" escapeXml="${!nkfn:zkontrolujKomentar(k.komentar)}" />
Další možností je, nevypsat chybné komentáře vůbec a zobrazit místo nich hlášku. Tento kód najdete v mercurialu v historii verzí souboru komentareVypis.tag. Jestli kontrolovat data jen na vstupu nebo i na výstupu, je z velké části kompromis mezi bezpečností a výkonem. S nastavením můžete experimentovat a vyzkoušet si, jestli se zapnutá validace vůbec na výkonu nějak viditelně projeví.
Závěr
Dnes jme do naší aplikace doplnili podporu komentářů a ukázali jsme si ještě jeden způsob, jak lze provázat aplikace – byť ne tak sofistikovaný jako webové služby, REST nebo jiné standardnější postupy. Také jsme nakousli téma XML a jeho validace v Javě. Jen připomínám, že aplikaci si můžete vyzkoušet na adrese nekuřák.net a jméno a heslo je: zdrojak.root.cz/heslo – pokud si nechcete zakládat vlastní účet.
Odkazy
- Texy! – PHP knihovna pro snadné formátování textu.
- JTexy – její port do jazyka Java.
- XML schémata – stránka Jiřího Koska o XML schématech.
- Libxml2 – XML parser a sada nástrojů napsaná v C.
- Relax NG – další způsob definice XML dokumentů.
- Schematron – totéž, ale trochu jinak.
Školení Google Analytics pro pokročilé

- Jak využít nové funkce Google Analytics
- Vyhodnocování kampaní díky používání Multichannel funnels
- Kde návštěvníci vašeho webu utíkají z objednávacího procesu.
- Nebudete opakovat časté chyby při vyhodnocování dat o návštěvnosti.
Detailní informace o školení Google Analytics pro pokročilé »
Seriál Java na webovém serveru
- Java na webovém serveru: SOAP webové služby
- Java na webovém serveru: hlasování a grafy v SVG
- Java na webovém serveru: Komentáře a integrace s Texy
- Java na webovém serveru: AJAX formuláře
- Java na webovém serveru: implementujeme Jabber
Přehled názorů
Tento text je již více než dva měsíce starý. Chcete-li na něj reagovat v diskusi, pravděpodobně vám již nikdo neodpoví. Pro řešení aktuálních problémů doporučujeme využít naše diskusní fórum.
