FK~

Moje odkazy

Ostatní odkazy

Close Windows
Nenajdete mě na Facebooku ani Twitteru
Rozpad EU
Jsem členem FSF
There Is No Cloud …just other people's computers.
Sane software manifesto / Manifest příčetného softwaru

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

vydáno: 23. 3. 2010 15:19, aktualizováno: 18. 7. 2020 13:42

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.

Logo OpenJDK (průhledné 1)

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.

Nastavení e-mailového spojení.

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“.

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 svobodný 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í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>

Kaptcha – ochrana proti spamu.

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 kousekm 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 a zdroje:

Témata: [Java]

Komentáře čtenářů

Za tento článek zatím nikdo autora nekamenoval

Přidat komentář

reagujete na jiný komentář (zrušit)
jméno nebo přezdívka
název příspěvku
webová stránka, blog
e-mailová adresa
nápověda: možnosti formátování
ochrana proti spamu a špatným trollům

Náhled komentáře