Posts tagged: captcha

SimpleCaptcha….not too simple.

devme - captchaL’ultima volta ci siamo lasciati con la promessa di rincontrarci per parlare del captcha, l’ormai famoso sistema che rende più sicure le nostre procedure web guidate. Per dovere di cronaca, sinteticamente, segue una breve descrizione. Il captcha è un sistema che genera un immagine casuale, contenente caratteri alfanumerici, non interpretabili da alcuna procedura automatica. Per completare uno step di una qualunque procedura web che contiene un capthca, è necessario inserire ciò che l’immagine raffigura in un campo di testo associato, in modo che server-side si possa effettuare il controllo. Se il contenuto dell’immagine differisce da ciò che è stato inserito nel campo di testo, allora la procedura fallisce; la procedura ha successo in caso contrario. Come contestualizziamo il tutto, in Spring Web Flow? Intanto scegliamo la libreria che implementa il captcha. Le papabili sono molte, la più citata in rete è JCapthca, che avevo anche utilizzato, se non fosse per i miei colleghi che non l’hanno voluta perché non gradivano le immagini generate dal punto di vista grafico. Quindi cercando un altro pò per la rete mi sono imbattuto in simplecatptcha. E’ una libreria molto light, semplice da utilizzare al tempo stesso efficace. Genera immagini carine, con alcuni effetti notevoli, quali ad esempio la distorsione che ho utilizzato. Let’s go. Al solito, si consideri una procedura di registrazione web composta, per semplicità da 3 step, inserimento dei dati personali, riepilogo e conferma. Scarichiamo e mettiamo nel classpath della nostra applicazione la libreria. Successivamente dobbiamo configurare il file web.xml in modo da "installare" il captcha, configurando l’apposita servlet che si occuperà di creare l’immagine casuale.

1
2
3
4
5
6
7
8
9
<servlet>
    <servlet-name>Captcha</servlet-name>
    <servlet-class>nl.captcha.servlet.CaptchaServlet</servlet-class>
     .....some setup of capthca....
</servlet>
<servlet-mapping>
    <servlet-name>Captcha>/servlet-name>
    <url-pattern>/Captcha.jpg>/url-pattern>
</servlet-mapping>

Sul sito di simplecaptcha troverete alcuni setup iniziali che si possono dare alle immagini generate, ad esempio, impostare un background, utilizzare la distorsione, forzare ciò che viene generato, etc, etc. Noterete che i setup sono della forma di tag del tipo init-param che vengono inseriti del tag servlet, lì dove vedete " …..some setup of capthca….". Ok, quindi ora possiamo usare la libreria, e non solo, se ci riferiamo all’interno delle nostre pagine all’indirizzo /Captcha.jpg invocheremo la libreria che si occuperà di generare l’immagine desiderata. Quindi metteremo un bel tag img nel primo step della nostra procudura:

1
2
3
4
5
6
7
8
9
10
<spring:bind path="LCCaptcha">
    <div class="field <c:if test="${status.error}">wrong</c:if>">
        <label for="LCCaptcha">Inserire la scritta che compare nell'immagine</label>
        <img src="<c:url value="Captcha.jpg" />" title="Captcha" alt="Captcha">
        <input type="text" id="LCCaptcha" name="LCCaptcha" value="">
        <c:if test="${status.error}">
            <div class="error">${status.errorMessage}</div>
        </c:if>
    </div>
</spring:bind>

Dando per scontato di conoscere Spring Web Flow e quindi Spring, notiamo che l’elemento img si trova dentro un elemento spring:bind che si preoccupa al submit della pagina di fare bind del contenuto del campo di input (in questo caso) con la corrispondente varibile del bean associato. La descrizione precisa del funzionamento di SWF esula dal contenuto del post. Ciò detto, vediamo che l’url dell’immagine corrisponde all’invocazione dell’indirizzo, configurato all’interno del web.xml che invoca la libreria del captcha. Risultato vedremo l’immagine generata, che cambierà ad ogni ricarica della pagina. Contestualmente alla generazione dell’immagine, la libreria simplecaptcha genererà il valore di ciò che viene visualizzato nell’immagine, rendendolo disponibile come attributo di sessione, dalla chiave SIMPLE_CAPCHA_SESSION_KEY. Supponiamo quindi di inserire i dati presenti nello step1 e fare submit della pagina, spostandoci quindi nel riepilogo ovvero allo step2 della procedura. Ricordiamo che utilizzando SWF per lo sviluppo di applicazioni web, è necessario definire un flusso di operazioni che specificano come avverrà la navigazione delle pagine. Ad esempio, nel flusso di navigazione verrà definito che dallo step1 si andrà allo step2, previa validazione dei dati. Di seguito mostriamo lo scorcio di codice xml del flusso di navigazione che definisce quanto detto sopra:

1
2
3
4
5
6
7
8
9
10
11
12
13
.....
.....
<view-state id="start" view="step1">
    <render-actions>
        <action method="setupForm" bean="DevMeAction" />
    </render-actions>
    <transition on="regUserStep1" to="step2">
        <action method="bindAndValidate" bean="DevMeAction" />
    </transition>
    <transition on="regUserCancel" to="userCancel" />
</view-state>
.....
....

Quindi da quanto scritto sopra, si ha che al momento del submit dei dati, andremo allo step2 se la procedura di validazione andrà a buon fine. In caso contrario, rimarremo nella stessa view, visualizzando opportunamente i messagi di errori dei campi non validi. Qui ho usato un piccolo trick, se così possiamo chiamarlo, non sapendo se è la soluzione migliore nell’ambito SWF. Subito prima di chiamare il metodo che effettua la validazione del form, SWF richiama un suo metodo interno doBind(…) che si preoccupa di prendere i dati nel form e assegnarli alle corrispondenti variabili definite all’interno del bean associato al form…il tutto avviene in automatico. Facendo override del metodo, l’idea è quella di recuperare il valore dalla sessione e assegnarlo, nel mio caso ad una variabile del bean, ma nulla vieta di creare un attributo del flusso e assegnarlo.

1
2
3
4
5
6
7
8
9
10
11
12
@Override
protected void doBind(RequestContext context, DataBinder binder) throws Exception {
    super.doBind(context, binder);
    /* Get user data from flow */
    DevMeBean devme = (DevMeBean) context.getFlowScope().get("devme");
    /* Retrive session value only in step 2*/
    String captcha_id = context.getExternalContext().getSessionMap().
                        getRequiredString("SIMPLE_CAPCHA_SESSION_KEY");
    if (captcha_id!=null) {
        user.setLCCaptcha_id(captcha_id);
    }
}

Per il mio caso risulta più comodo averlo nel bean, così poi il metodo di validazione, cha ha visibiltà del bean, può accedere comodamente sia al valore del campo di input e sia al valore dell’attributo di sessione, in modo da poter effettuare il controllo.

1
2
3
4
5
6
7
8
public void validate(Object target, Errors errors) {
    DevMeBean devme = (DevMeBean ) target;
    String captcha_id = devme.getLCCaptcha_id();
    String captcha = devme.getLCCaptcha();
    if (!captcha_id.equals(captcha)) {
        errors.rejectValue("LCCaptcha", "error.captcha.invalid");
    }
}

E il gioco è fatto. Se tutto va bene, e quindi niente errori, allora si va allo step2 della procedura….altrimenti si rimane allo step1, e si visualizza l’errore. A questo punto vi chiedere, bhe se tutto è così semplice allora perchè nel titolo hai messo "…not too simple" ? Ora vi dico. L’applicazione a cui sto lavorando dovrà girare su un sistema Linux Debian senza x-window, ovvero senza ambiente grafico. Tipicamente le applicazioni Java che hanno a che fare con la manipolazione delle immagini si appoggiano sul sottosistema grafico del sistema operativo sul quale girano. Se nessun sottosistema è installato, allora bisogna informare la JVM e lanciarla in esecuzione con una specifica opzione:

1
-Djava.awt.headless=true

, è possibile reperire questo dettaglio direttamente dal sito di simplecaptcha. Naturalmente ho fatto quanto detto, ma ovviamente la libreria non né voleva sapere di funzionare, e quindi l’immagine non veniva visualizzata. Gira che ti rigira, ho trovato che simplecaptcha, che premetto è alla versione 0.3, ha un piccolo bug. Per risolverlo è necessario scaricare i sorgenti, e rifare il deploy dell’applicazione, con 1000 sbattimenti annessi al recupero di alcune librerie che non sono, diciamo così standard. Il bug lo potete trovare direttamente sul forum qui, ma vi assicuro che il jar bug-free no, quindi lo rendo disponibile per dovere informatico. See you later.

WordPress Themes