<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Christoph Bünte &nbsp;&raquo; Software Entwicklung Berlin</title>
	<atom:link href="http://www.christophbuente.de/tag/jcaptcha/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.christophbuente.de</link>
	<description>Software Entwicklung</description>
	<lastBuildDate>Tue, 07 Dec 2010 11:30:07 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.5</generator>
		<item>
		<title>captcha tutorial &#8211; Bots aussperren</title>
		<link>http://www.christophbuente.de/2007-10-23-bots-aussperren-mit-captcha/</link>
		<comments>http://www.christophbuente.de/2007-10-23-bots-aussperren-mit-captcha/#comments</comments>
		<pubDate>Tue, 23 Oct 2007 07:30:55 +0000</pubDate>
		<dc:creator>Christoph Bünte</dc:creator>
				<category><![CDATA[Java JEE]]></category>
		<category><![CDATA[Software Entwicklung]]></category>
		<category><![CDATA[bot aussperren]]></category>
		<category><![CDATA[captcha]]></category>
		<category><![CDATA[integration]]></category>
		<category><![CDATA[J2EE]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[jcaptcha]]></category>
		<category><![CDATA[JEE]]></category>
		<category><![CDATA[jsp]]></category>
		<category><![CDATA[session id]]></category>
		<category><![CDATA[Web Entwicklung]]></category>

		<guid isPermaLink="false">http://www.christophbuente.de/2007/10/23/bots-aussperren-mit-captcha/</guid>
		<description><![CDATA[Vergangene Woche habe ich für einen aktuellen Kunden eine Funktion implementiert, die es dem Nutzer einer Webseite erlaubt, anderen diese Seite per E-Mail zu empfehlen. Dies öffnete Spammern potentiell Tür und Tor. Dem Kunden war besonders daran gelegen, sogenannte Bots auszusperren, die diese Funktion in sehr hoher Frequenz ausführen können. [ad#vert-banner] Als Mittel der Wahl [...]]]></description>
			<content:encoded><![CDATA[<p>Vergangene Woche habe ich für einen aktuellen Kunden eine Funktion implementiert, die es dem Nutzer einer Webseite erlaubt, anderen diese Seite per E-Mail zu empfehlen. Dies öffnete Spammern potentiell Tür und Tor. Dem Kunden war besonders daran gelegen, sogenannte Bots auszusperren, die diese Funktion in sehr hoher Frequenz ausführen können.</p>
<p><span id="more-16"></span></p>
<p>[ad#vert-banner]</p>
<p>Als Mittel der Wahl hat sich seit Jahren das sogenannte <acronym title="Completely Automated Public Turing Test to tell Computer and Humans Apart" lang="en"><a href="http://de.wikipedia.org/wiki/Captcha">CAPTCHA</a></acronym> etabliert. Dabei werden Bilder erzeugt, die auf unebendem Hintergrund verzerrte Zeichen darstellen. Der Benutzer muss diese sogenannte Challenge lösen, in dem er die Zeichenfolge in ein Formularfeld eingibt. Stimmen die eingegebenen Zeichen und die Zeichen auf dem Bild übereinstimmen. Hier macht man sich zu nutze, dass der Mensch besser abstrahieren kann, und die Buchstaben trotz widriger Umstände erkennen kann, wo eine Maschine scheitert.</p>
<p>Da ich das Rad nicht neu erfinden will, habe ich mich einer Bibliothek bedient, die diese Funktionalität zur Verfügung stellt, und sehr einfach in das aktuelle Projekt einzubinden ist. Meine Wahl fiel auf <a href="http://jcaptcha.sourceforge.net/">JCaptcha</a>. Wie der Name vermuten läßt ist JCaptcha eine Java basierte Implementierung. Sie läßt sich in Java basierte Webprojekte sehr einfach integrieren.</p>
<p>Folgende Schritte sind notwendig:</p>
<ol>
<li>Implementierung eines eigenen Servlets, welches mit der Auslieferung der generierten Bilder beauftragt wird.</li>
<li> Eintrag des Servlets und des Mappings in die web.xml</li>
<li> Implementierung eines Singleton, welches den CaptchaService zur Verfügung stellt.</li>
<li>Erstellen einer validierungsmethode zum Überprüfen der Benutzereingabe.</li>
</ol>
<p>Der generelle Ablauf ist recht einfach. Das in der JSP mit Hilfe des &lt;img src=&#8221;img.captcha&#8221;&gt; referenzierte Bild wird von unserem Servlet bedient. Dieses erzeugt mit Hilfe des CaptchaService ein neues Bild und gibt es als jpg zurück.  Der CaptchaService speichert in einer internen Map unter dem Schlüssel der jeweiligen session id das captcha response. Die Methode validateResponseForID des CaptchaService prüft, ob der eingegebene Text mit dem Bild übereinstimmt.</p>
<p>Das Servlet wird wie folgt implementiert:</p>
<pre lang="java">
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Locale;import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.octo.captcha.service.CaptchaServiceException;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
/**
 * @author Christoph Bünte
 *
 * @version $Revision: 1.5 $
 *
 * @created 11.10.2007
 *
 */
public class CaptchaServlet extends HttpServlet {

/**
  * The serial id for this class
  */
 private static final long serialVersionUID = -5555311841435084305L;

@Override
 public void init(ServletConfig servletConfig) throws ServletException {

super.init(servletConfig);
}

/**
  * Generiert das Captcha image in form eines bytearrays
  *
  * @param token
  *            SessionId
  * @param loc
  *            Locale Object für die Sprache des Benutzers
  * @return
  */
 private synchronized byte[] getCaptchaChallenge(String token, Locale loc) {

byte[] captchaChallenge = null;

try {
 		// create the captcha challenge
 		BufferedImage challenge = CaptchaServiceSingleton.getInstance()
 				.getImageChallengeForID(token, loc);

		// transform image data into jpeg byte array
 		ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();

		JPEGImageEncoder jpegEncoder = JPEGCodec
 				.createJPEGEncoder(jpegOutputStream);

		jpegEncoder.encode(challenge);

		captchaChallenge = jpegOutputStream.toByteArray();

 	} catch (IOException e) {
 		//TODO: Fehlerbehandlung
 	} catch (CaptchaServiceException e) {
 		//TODO: Fehlerbehandlung
 	}

	return captchaChallenge;

 }

@Override
 protected void doGet(HttpServletRequest request,

 		HttpServletResponse response) throws ServletException, IOException {

			// Hier benutzen wir die session id und das Locale Objekt des requests
			String token = request.getSession().getId();
 			Locale loc = request.getLocale();

			// hole das CAPTCHA challenge als JPEG
			byte[] captchaAsJpeg = getCaptchaChallenge(token, loc);

			// falls nicht verfügbar, sende http code 404 (resource not available)
			if (captchaAsJpeg == null) {
				response.sendError(HttpServletResponse.SC_NOT_FOUND);
			return;
			}

			// gibt ein response Objekt mit dem jpeg bild zurück
			// (für den browser: benutze keinen cache und verwirf den inhalt sofort)
			// set header

			response.setHeader("Cache-Control", "no-store");
			response.setHeader("Pragma", "no-cache");
			response.setDateHeader("Expires", 0);
			response.setContentType("image/jpeg");

			// sende bilddaten
			ServletOutputStream responseOutputStream = response.getOutputStream();
			responseOutputStream.write(captchaAsJpeg);
			responseOutputStream.flush();
			responseOutputStream.close();
 		}

}</pre>
<p>Der Eintrag in die web.xml sieht so aus:</p>
<pre class="xml">
&lt;servlet&gt;
	&lt;servlet-name&gt;jcaptcha&lt;/servlet-name&gt;
	&lt;display-name&gt;JCaptcha&lt;/display-name&gt;
	&lt;servlet-class&gt;your.package.path.CaptchaServlet&lt;/servlet-class&gt;
	&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
&lt;/servlet&gt;</pre>
<pre class="xml">&lt;servlet-mapping&gt;
	&lt;servlet-name&gt;jcaptcha&lt;/servlet-name&gt;
	&lt;url-pattern&gt;*.captcha&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;</pre>
<pre class="xml"></pre>
<p>Das CaptchaService als Singleton implementiert:</p>
<pre lang="java">
import com.octo.captcha.engine.image.ListImageCaptchaEngine;
import com.octo.captcha.service.captchastore.CaptchaStore;
import com.octo.captcha.service.captchastore.FastHashMapCaptchaStore;
import com.octo.captcha.service.image.DefaultManageableImageCaptchaService;
import com.octo.captcha.service.image.ImageCaptchaService;</pre>
<pre lang="java">/**
 *
 * @author Christoph Bünte
 *
 * @version $Revision: 1.6 $
 *
 * @created 11.10.2007
 *
 */
public class CaptchaServiceSingleton {

	private static ImageCaptchaService instance = new DefaultManageableImageCaptchaService();

	/**
	  * Gibt die einzige Instanz einnes CaptchaService zurück.
	  *
	  * @return
	  */
	 public static ImageCaptchaService getInstance() {

		return instance;
	 }
}</pre>
<p>Im Formular für die Eingabe der Captcha Response, wird das Bild wie folgt eingebunden:</p>
<pre class="html">
&lt;img src="img.captcha"  class="captcha"&gt;</pre>
<p>Je nachdem, welches Webframework man verwendet, differiert die Implementierung einer Validierungsmethode. Letztlich könnte sie wie folgt aussehen. Es ist ein Beispiel, wie es im <a href="http://mc4j.org/confluence/display/stripes/Home">stripes framework</a> verwendet wird.</p>
<pre lang="java">/**
 * Validating capture input
 *
 * @param errors
 */
@ValidationMethod(on = { "sendRecommendation" }, when = ValidationState.ALWAYS)

public void validateCaptcha(ValidationErrors errors) {
 try {
 	String sessionId = context.getRequest().getSession().getId();
 	if (!CaptchaServiceSingleton.getInstance().validateResponseForID(
 			sessionId, captchaResponse)) {
 		errors.add("captchaResponse", new LocalizableError(
 				"captchaResponse.invalidValue"));
 	}
 } catch (CaptchaServiceException e) {
 	// ungültige Session id, Fehlerbehandlung
 }
}</pre>
<p>Das Ergebnis ist schon ganz ansehnlich. Im Formular wird mit der Default Engine ein mehr oder weniger schönes Captcha Bild erzeugt. Jedoch fällt Ästheten sofort auf, dass das Bild überhaupt nicht in das Design der Seite passt. Um solche Anforderungen zu erfüllen, implementiert man am besten eine CaptchaEngine. Dazu müssen wir die Singleton Klasse noch etwas modifizieren, doch das schnell erledigt. Wird sind doch agil, oder?</p>
<p>Die unten gezeigte Engine erzeugt ein Captcha Bild mit hellblauem Hintergrund und grauer und schwarzer Schrift. Für die Schrift werden nur Großbuchstaben verwendet. Man hätte als Alternative auch die Informationen zum Rendern des Bildes auch direkt in der Singleton Klasse zu einer vorhandenen Engine hinzufügen können. Unangenehmer Nebeneffekt ist aber, dass es immer eine Default engine gibt. Beim Rendern der Bilder wird dann eine verfügbare Engine zufällig ausgewählt, so dass in unregelmässigen Abständen eine Bild mit grün-rot-gelb gesprenkeltem Hintergrund erscheint. Dieses, für mich anfänglich, merkwürde Verhalten hat mich zwei Stunden aufgehalten. Wer unterschiedliche Captcha Bilder erzeugen möchte implenentiert entsprechend mehr Klassen. Hier ein Beispiel:</p>
<pre lang="java">
import java.awt.Color;import com.octo.captcha.component.image.backgroundgenerator.BackgroundGenerator;
import com.octo.captcha.component.image.backgroundgenerator.UniColorBackgroundGenerator;
import com.octo.captcha.component.image.fontgenerator.DeformedRandomFontGenerator;
import com.octo.captcha.component.image.fontgenerator.FontGenerator;
import com.octo.captcha.component.image.textpaster.RandomTextPaster;
import com.octo.captcha.component.image.textpaster.TextPaster;
import com.octo.captcha.component.image.wordtoimage.ComposedWordToImage;
import com.octo.captcha.component.image.wordtoimage.WordToImage;
import com.octo.captcha.component.word.wordgenerator.RandomWordGenerator;
import com.octo.captcha.component.word.wordgenerator.WordGenerator;
import com.octo.captcha.engine.CaptchaEngine;
import com.octo.captcha.engine.image.ListImageCaptchaEngine;
import com.octo.captcha.image.ImageCaptchaFactory;
import com.octo.captcha.image.gimpy.GimpyFactory;

/**
 * Das ist unsere spezielle {@link CaptchaEngine} Implementierung.
 * @author Christoph Bünte
 *
 * @version $Revision: 1.2 $
 *
 * @created 12.10.2007
 *
 */
final public class CustomCaptchaEngine extends ListImageCaptchaEngine {

@Override
 protected void buildInitialFactories() {

// RGB: #4C4E42
 	Color textColor = new Color(0x4c, 0x4e, 0x42);
 	Color textColor2 = Color.BLACK;
 	Color[] colors = new Color[2];
 	colors[0]=textColor;
 	colors[1]=textColor2;

// RGB: #F4F8FB
 	Color backgroundColor = new Color(0xf4, 0xf8, 0xfb);

// Einheitlicher Hintergrund mit der gegeben Breite, Höhe und Farbe
 	BackgroundGenerator bgGenerator = new UniColorBackgroundGenerator(250,
 			70, backgroundColor);

// Der Text hat die Min-und Maxlänge mit den gegeben Farben
 	TextPaster paster = new RandomTextPaster(6, 6, colors);

// Schriftgrößen 10 bis 30
 	FontGenerator fontGenerator = new DeformedRandomFontGenerator(
 			10, 30);

// Benutze einen einfachen Mechanismus zum Mischen von Hintergrund und Text
 	WordToImage wordToImage = new ComposedWordToImage(fontGenerator,
 			bgGenerator, paster);

// Benutze zufällige Worte aus den gegebenen Buchstaben
 	WordGenerator wordGenerator = new RandomWordGenerator(
 			"ABCDEFGHIJKLMNOPQRSTUVWXYZ");

ImageCaptchaFactory factory = new GimpyFactory(wordGenerator,
 			wordToImage);

 	this.addFactory(factory);
 }
}</pre>
<p>Und hier die neue Singleton Implementierung:</p>
<pre lang="java">
import com.octo.captcha.engine.image.ListImageCaptchaEngine;
import com.octo.captcha.service.captchastore.CaptchaStore;
import com.octo.captcha.service.captchastore.FastHashMapCaptchaStore;
import com.octo.captcha.service.image.DefaultManageableImageCaptchaService;
import com.octo.captcha.service.image.ImageCaptchaService;

/**
 *
 * @author Christoph Bünte
 *
 * @version $Revision: 1.6 $
 *
 * @created 11.10.2007
 *
 */

public class CaptchaServiceSingleton {

	private static ImageCaptchaService instance = initializeService();

	/**
	  * Gibt die einzige Instanz einnes CaptchaService zurück.
	  *
	  * @return
	  */
	 public static ImageCaptchaService getInstance() {
	 	return instance;
	 }

	/**
	  * Initialisiert den {@link ImageCaptchaService}
	  *
	  * @return
	  */
	 private static ImageCaptchaService initializeService() {

	// Wir brauchen eine Instanz unser eigenen Engine
	 	ListImageCaptchaEngine engine = new CustomCaptchaEngine();

		CaptchaStore captchaStore = new FastHashMapCaptchaStore();
 		captchaStore.empty();

		ImageCaptchaService service = new DefaultManageableImageCaptchaService(
 			captchaStore, engine, 180, 100000, 75000);

		return service;

 	}

}</pre>
<p>Kommentare und Anregungen sind wie immer willkommen. Beispiele für die kreativsten Bilder nehme ich auch gerne entgegen. Viel Spass beim ausprobieren.</p>
<div id="crp_related"><h3>Ähnliche Beiträge:</h3><ul><li><a href="http://www.christophbuente.de/2008-01-24-stripes-framework-tests-fur-actionbeans-im-wizard-modus/" rel="bookmark" class="crp_title">Stripes Framework &#8211; Tests für ActionBeans im Wizard Modus</a></li><li><a href="http://www.christophbuente.de/2007-11-10-paypal-subscriptions-abonnements-in-java-integrieren/" rel="bookmark" class="crp_title">Paypal subscriptions &#8211; Abonnements in Java integrieren</a></li><li><a href="http://www.christophbuente.de/2007-12-21-voiceglue-konfiguration-server-fur-sprachanwendungen-einrichten/" rel="bookmark" class="crp_title">VoiceGlue Konfiguration &#8211; Server für Sprachanwendungen einrichten</a></li><li><a href="http://www.christophbuente.de/2008-08-04-bilder-versehentlich-geloscht-urlaubserinnerungen-ganz-leicht-zuruckholen/" rel="bookmark" class="crp_title">Bilder versehentlich gelöscht &#8211; Urlaubserinnerungen ganz leicht zurückholen</a></li><li><a href="http://www.christophbuente.de/2009-10-29-opensocial-gadgets-apps-fur-studivz-selbst-entwickeln/" rel="bookmark" class="crp_title">Opensocial Gadgets &#8211; Apps für StudiVZ selbst entwickeln</a></li></ul></div>]]></content:encoded>
			<wfw:commentRss>http://www.christophbuente.de/2007-10-23-bots-aussperren-mit-captcha/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using memcached (Feed is rejected)
Page Caching using memcached
Database Caching 2/13 queries in 0.015 seconds using memcached
Object Caching 466/486 objects using memcached

Served from: www.christophbuente.de @ 2012-02-05 04:02:27 -->
