Java na webovém serveru: Komentáře a integrace s Texy

java perex

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 (16 dílů)

  1. Java na serveru: úvod 8.1.2010
  2. Java na webovém serveru: první web 15.1.2010
  3. Java na webovém serveru: práce s databází 29.1.2010
  4. Java na webovém serveru: práce s databází II 12.2.2010
  5. Java na webovém serveru: lokalizace a formátování 19.2.2010
  6. Java na webovém serveru: autorizace a autentizace 26.2.2010
  7. Java na webovém serveru: autorizace a autentizace II 5.3.2010
  8. Java na webovém serveru: porovnání Javy a PHP 10.3.2010
  9. Java na webovém serveru: Vlastní JSP značky a servlety 17.3.2010
  10. Java na webovém serveru: posílání e-mailů a CAPTCHA 24.3.2010
  11. Java na webovém serveru: píšeme REST API 7.4.2010
  12. Java na webovém serveru: SOAP webové služby 14.4.2010
  13. Java na webovém serveru: hlasování a grafy v SVG 28.4.2010
  14. Java na webovém serveru: Komentáře a integrace s Texy 9.6.2010
  15. Java na webovém serveru: AJAX formuláře 23.6.2010
  16. Java na webovém serveru: implementujeme Jabber 30.6.2010

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/hes­lo – 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.

Franta Kučera působí jako Java vývojář na volné noze. Programování je jeho koníčkem už od dětství. Kromě toho má rád Linux, relační SŘBD a XML.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 29

Přehled komentářů

harvie PHP interface
tom Re: PHP interface
František Kučera Re: PHP interface
František Kučera Re: PHP interface
snowracer Texy Quercus
lolek java na web
jehovista Re: java na web
jerzy.burzek Re: java na web
Flasi Re: java na web
lolek Re: java na web
jehovista Re: java na web
Radek Miček Re: java na web
blizzboz Re: java na web
x Re: java na web
Přezdívka Re: java na web
Palo Re: java na web
Ondra Větších webů v Javě...
Radek Miček Re: java na web
dívka Re: java na web
Radek Miček Re: java na web
František Kučera Re: java na web
Radek Miček Re: java na web
František Kučera Re: java na web
Satai Re: java na web
KapitánRUM Prosba
KapitánRUM Re: Prosba
lopata chybí syntax coloring
Martin Malý Re: chybí syntax coloring
Ondra JTexy je ve vývoji...
Zdroj: https://www.zdrojak.cz/?p=3253