Přejít k navigační liště

Zdroják » Různé » Java na webovém serveru: posílání e-mailů a CAPTCHA

Java na webovém serveru: posílání e-mailů a CAPTCHA

Články Různé

Dnes se opět budeme věnovat praktickým ukázkám a naší aplikaci. Po předchozím díle o vlastních JSP značkách a servletech se dnes podíváme na to, jak z Javy na serveru posílat e-maily a jak chránit aplikaci proti spamu pomocí tzv. CAPTCHA.

Zatímco pro práci s e-maily si vystačíme se standardními prostředky platformy Java, pro implementaci anti-spamové ochrany budeme muse použít cizí knihovnu. Znalosti servletů probírané minule se nám budou hodit, protože CAPTCHA, kterou dnes použijeme, je založená právě na servletu.

Jen pro připomenutí: jako obvykle si stáhneme aktuální zdrojové kódy z Mercurialu (případně jako bzip2 archiv přes web):

$ hg pull
$ hg up "10. díl"

Posílání e-mailů

Ve webových aplikacích potřebujeme poměrně často posílat e-maily – ať už se jedná o zapomenutá hesla, upozornění správcům nebo třeba registrační e-maily. Vytvoříme si proto univerzální EJB komponentu, která zapouzdří odesílání zpráv, a následně upravíme proces registrace uživatele tak, aby zároveň odeslal upozornění uživateli, že byl zaregistrován.

Pro práci s elektronickou poštou budeme používat standardní JavaMail API. Nakonfigurujeme si proto v aplikačním serveru tzv. JavaMail Session a přiřadíme jí JNDI jméno.

Java 9

Důležité je vyplnit adresu SMTP serveru (v našem případě localhost) a výchozí adresu odesílatele.

V EJB komponentě získáme instanci JavaMail Session jednoduše pomocí anotace @Resource  – nemusíme tak konfigurovat javax.mail.Session ručně v naší aplikaci.

@Resource(lookup = "mail/nekurak.net")
Session smtpRelace;

Pokud bychom např. chtěli odesílat přes jiný SMTP server, stačí překonfigurovat nastavení mail/nekurak.net na úrovni aplikačního serveru a není potřeba zasahovat do aplikace (nasazovaný .war nebo .ear je pořád stejný).

Obecná metoda pro odeslání e-mailu se nachází ve třídě Postak a vypadá následovně:

public void odesliZpravu(Adresa komu, Adresa od, String predmet, String text) throws NekurakVyjimka {

    try {
        MimeMessage mimeZprava = new MimeMessage(smtpRelace);

        mimeZprava.addRecipient(RecipientType.TO, komu.getInternetAddress());
        if (od != null) {
        mimeZprava.setFrom(od.getInternetAddress());
        }
        mimeZprava.setSubject(predmet);
        mimeZprava.setText(text, "UTF-8");

        Transport.send(mimeZprava);
        log.info("Zpráva pro " + komu + " byla odeslána.");
    } catch (Exception e) {
        throw new NekurakVyjimka("Selhalo odesílání e-mailu pro: " + komu, e);
    }
}

Tuto metodu používáme ve třídě UzivatelEJB k odeslání e-mailu uživateli, který se právě zaregistroval (jen tehdy, pokud vyplní e-mail). Zprávy se posílají lokalizované do jazyka, který měl uživatel nastavený ve chvíli vyplňování registračního formuláře.

JMS – ano či ne?

V diskusi pod jedním z předchozích dílů se objevila myšlenka použít pro odesílání e-mailů JMS (Java Message Service). V praxi by to znamenalo, že by naše aplikace nepoužívala klasické JavaMail rozhraní, ale předala by e-mailovou zprávu v podobě objektu do nějaké JMS fronty a zpráva by se „někde jinde“ z JMS převzala a předala SMTP serveru.

Na otázku zda tento přístup uplatnit neexistuje jednoznačná odpověď. Argumentem pro JMS je to, že přijetí e-mailu SMTP serverem může nějakou dobu trvat, což by zdržovalo načtení stránky – takže by mohlo být vhodné použít JMS frontu a předání SMTP serveru provést asynchronně. Na druhou stranu SMTP server na lokálním stroji přijme zprávu prakticky okamžitě a zapojovat do procesu JMS znamená další článek řetězce, který se může pokazit. Záleží tedy hlavně na tom, jakou infrastrukturu máme vybudovanou – pokud nám na serveru běží SMTP server, nemá moc smysl zapojovat JMS. A obráceně: pokud máme fungující JMS systém, nemusíme na server instalovat SMTP server a můžeme použít JMS pro předání zpráv na jiný server, kde SMTP už máme (jenže i v tom případě stačí překonfigurovat JavaMail Session v aplikačním serveru, aby se nespojovala s localhostem, ale s jiným strojem).

Použít JMS pro prosté obalení e-mailů, jako transportní protokol místo SMTP bude ve většině případů nadbytečné. Smysluplnější je nepoužívat JMS pro nízkoúrovňové (technické) události typu „pošli e-mail“, ale pro události obchodního (byznys) charakteru typu „založ uživatele“.

Akademie Root vás zve na školení Základy programovacího jazyka Java a na následné školení Pokročilejší kurz jazyka Java.

CAPTCHA – ochrana proti spamu

Internet je bohužel plný spamerů, a tak je potřeba většinu formulářů chránit proti jejich nevyžádaným reklamám – obvykle pomocí tzv. CAPTCHA (completely automated public Turing test to tell computers and humans apart), což je test, který nám umožní rozlišit legitimní uživatele (živé lidi) od spamovacích robotů.

Kaptcha

Technologie JSP jako taková nemá vestavěnou podporu pro CAPTCHu, je tedy potřeba použít nějakou dodatečnou knihovnu. Jednou z implementací CAPTCHy v Javě je Kaptcha. Na jejím příkladě si ukážeme začlenění proti-spamové ochrany do webové aplikace.

Tato knihovna je otevřený software licencovaný pod Apache 2.0 licencí. Stáhneme si ji z jejích stránek hostovaných na Google Code a přidáme si příslušný .jar do webového modulu aplikace nekurak.net-web.

Kaptcha není jen knihovna, která umí generovat obrázky s různě pokřiveným textem – poskytuje nám i servlet, který slouží ke zpřístupnění těchto obrázků na webu. Tento servlet si aktivujeme přidáním následujících řádků do souboru web.xml:

<servlet>
    <servlet-name>Kaptcha</servlet-name>
    <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>Kaptcha</servlet-name>
    <url-pattern>/kaptcha.jpg</url-pattern>
</servlet-mapping>

Do HTML formuláře, který chceme ochránit, pak přidáme CAPTCHu pomocí tohoto kódu:

<img src="kaptcha.jpg" alt="ochrana proti spamu"/>
<label>Opište kód: <input type="text" name="kaptcha" maxlength="255"/></label>

Java 9

Jednoduše vložíme obrázek a políčko formuláře. Servlet při generování obrázku nastavuje atribut relace KAPTCHA_SESSION_KEY na hodnotu, kterou právě vygeneroval. Po odeslání formuláře si tak můžeme zkontrolovat, zda uživatel opsal správný text:

<c:choose>
    <c:when test="${sessionScope['KAPTCHA_SESSION_KEY'] == param.kaptcha}">
    <p class="informacniHlaska">Správně opsaný kód z obrázku.</p>
    </c:when>
    <c:otherwise>
    <p class="chybovaHlaska">Špatně obsaný kód z obrázku.</p>
    </c:otherwise>
</c:choose>

Jelikož se někdy může vygenerovat špatně čitelný text, je dobré dát uživateli možnost přegenerovat si obrázek aniž by musel obnovovat celou stránku. Toho lze dosáhnout tímto kouskem JavaScriptu:

<script type="text/javascript">
    $(function(){
        $('#kaptchaIMG').click(function () { $(this).attr('src', 'kaptcha.jpg?' + Math.floor(Math.random()*100) ); })
    });
</script>

Používáme zde JavaScriptovou knihovnu jQuery, kterou jsme si do naší aplikace přidali už v minulém díle kvůli prohlížečce obrázků. Uživateli tak stačí kliknout na nečitelný obrázek a vygeneruje se mu nový.

Celý příklad naleznete v souboru kaptcha.jsp a vyzkoušet si ho můžete na adrese http://nekurak­.net/kaptcha.jsp.

Knihovna Kaptcha generuje obrázky v poměrně dobré kvalitě (čitelné) a implementovat pomocí ní do svých stránek ochranu proti spamu je snadné. Bohužel ale trpí podobným neduhem jako CAPTCHA na mnoha webech. Pokud si totiž uživatel otevře více stejných formulářů najednou, v proměnné na serveru je uložena pouze hodnota naposledy vygenerovaného obrázku. Když se uživatel pokusí odeslat dříve otevřený formulář, server ho odmítne, přestože uživatel obrázek opsal správně. Řešením by bylo ukládat si všechny v poslední době vygenerované kódy nebo tyto kódy na straně serveru vůbec neukládat a využít místo toho hashování a tajný klíč známý jen serveru.

Závěr

V dnešním díle jsme se naučili posílat e-maily z EJB komponenty a ukázali jsme si, jak ochránit naši aplikaci proti spamu pomocí knihovny Kaptcha. Příště se budeme věnovat tvorbě REST API pro naši aplikaci.

Odkazy

Komentáře

Odebírat
Upozornit na
guest
4 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
Zobrazit všechny komentáře
xtonda

1. Z jakého důvodu je ta universální navržena jako EJB? Přijde mi, že to není nutné, my to máme prostě jako běžnou utility třídu.

2. Odesílání přes JMS používáme docela dlouho a dost se to osvědčilo, JMS běžící na stejném či sousedním serveru je daleko dostupnější a spolehlivější než SMTP rozhraní Exchange serveru kdesi, odesílající aplikace je rychle vyřízena a nemusí řešit nedostupnost SMTP serveru.

xtonda

Lookup session z JNDI je na pár řádků, doslova. Případně i na to se dá udělat nějaký helper, navíc jsme to navrhovali pro Javu 1.4.

U nás se občas stane, že některý z Exchange serverů je nedostupný, blbne konektivita, při přečíslování IP trvá než se občerství DNS, atp.

My máme to předání do JMS napsané jako vlastní JavaMail Transport, takže přepnutí „do JMS“/„přímo na SMTP“ by znamenalo jen změnit v konfiguraci JNDI jméno pro lookup JavaMail session. Takže když je potřeba napsat kód odesílající mail, můžeme použít vlastní zjednodušující API pro vytvoření a odeslání mailu nebo, pokud potřebuji něco speciálního, můžeme použít přímo JavaMail, jedinou podmínkou je načíst JNDI jméno JavaMail session z konfigurace a použít tuto session.

Jak koukám na to vaše zapouzdření, tak možnost odeslání plaintext mailu jednomu příjemci je poněkud omezující.

exception

dobry den

natyka sa to priamo temy, ale aky ma podla vas vyznam pouzyvat genericke catched vynimky?
za hlavne nevyhody ich pouzytia povazujem najma to ze
1. nesuvisia priamo s biznis logikou metody, napriek tomu su castou jej signatury a kazia rozhranie
2. vynimka sa prenasa do dalsej hierarchie volania
stretnut sa s api kde kazda metoda deklaruje SpecificApiEx­ception nie je ojedinele
aky to ma potom vyznam?

otazka nebola smerovana ofenzivne, netyka sa priamo temy ale mna osobne viac ako funkcionalita na takychto clankoch zaujima sposob kodovania, ktory je casto podcenovany a pritom napr v pripade error handlingu sa nejedna vobec o trivialnu zalezitost, skor naopak

Přístupnost není jen o splnění norem: nový pohled na inkluzivní design

Přístupnost a inkluze možná nepatří mezi nejžhavější témata digitálního světa – dokud o nich nezačne mluvit Vitaly Friedman. Na WebExpo 2024 předvedl, že inkluzivní design není jen o splněných checkboxech, ale hlavně o lidech. S energií sobě vlastní obrátil zažité přístupy naruby a ukázal, že skutečně přístupný web je nejen možný, ale i nezbytný.

Efektivnější vývoj UI nebo API: Co si odnést z WebExpo 2025?

Různé
Komentáře: 0
Jak snadno implementovat moderní uživatelské rozhraní? Které funkce brzdí rychlost vašeho webu? A kdy raději sami přibrzdit, abychom využitím AI nepřekročili etické principy? Debatu aktuálních dev témat rozdmýchá sedmnáctý ročník technologické konference WebExpo, která proběhne v Praze od 28. do 30. května. Který talk či workshop si rozhodně nenechat ujít? Toto je náš redakční výběr z vývojářských hroznů.

Zapřáhněte AI jako nikdy předtím. Květnová konference WebExpo přivítá hvězdy technologického světa

Od 28. do 30. května 2025 promění pražský Palác Lucerna na tři dny technologická konference WebExpo. Na programu je více než 80 přednášek a workshopů od expertů z celého světa. WebExpo tradičně propojuje vývojáře, designéry, marketéry i byznysové lídry a nabízí praktické dovednosti, strategické myšlení a přináší nejnovější trendy nejen v oblasti AI.