Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Next »

This document describes integrating reCaptcha with CAS. The version of CAS used in this tutorial is 3.4.11.

login-webflow.xml

The following changes need to be made to WEB-INF/login-webflow.xml:

Replace view-state "viewLoginForm" with:
<view-state id="viewLoginForm" view="casLoginView" model="credentials">
	<binder>
    	<binding property="username" />
        <binding property="password" />
    </binder>
    <on-entry>
    	<set name="viewScope.commandName" value="'credentials'" />
    </on-entry>
    <transition on="submit" bind="true" validate="true" to="realSubmit">
    	<evaluate expression="authenticationViaFormAction.doBind(flowRequestContext, flowScope.credentials)" />
    </transition>
</view-state>
Replace action-state "realSubmit" with:
<action-state id="realSubmit">
	<evaluate expression="authenticationViaFormAction.submit(flowRequestContext, flowScope.credentials, messageContext)" />
	<transition on="warn" to="warn" />
	<transition on="success" to="captchaCheck" />
	<transition on="error" to="errorCount" />
</action-state>
Add action-states:
<action-state id="captchaValidate">
	<evaluate expression="captchaValidateAction"/>
    <transition on="success" to="sendTicketGrantingTicket" />
    <transition on="error" to="errorCount" />
</action-state>
    
<action-state id="errorCount">
	<evaluate expression="captchaErrorCountAction"/>
    <transition on="success" to="generateLoginTicket" />
</action-state>
Add decision-state:
<decision-state id="captchaCheck">
	<if test="flowScope.count != null &amp;&amp; flowScope.count >= 3" then="captchaValidate" else="sendTicketGrantingTicket"/>
</decision-state>

Implement CaptchaErrorCountAction

 

CaptchaErrorCountAction.java
package com.acme.cas.captcha;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;

public class CaptchaErrorCountAction extends AbstractAction {
    @Override
    protected Event doExecute(RequestContext context) throws Exception {
        int count;
        try {
            count = (Integer) context.getFlowScope().get("count");
        } catch (Exception e) {
            count = 0;
        }
        count++;
        context.getFlowScope().put("count", count);
        return success();
    }
}

 

Implement CaptchaValidateAction

 

CaptchaValidateAction.java
package com.acme.cas.captcha;
import javax.servlet.http.HttpServletRequest;
import net.tanesha.recaptcha.ReCaptchaImpl;
import net.tanesha.recaptcha.ReCaptchaResponse;
import org.apache.commons.lang.StringUtils;
import org.jasig.cas.web.support.WebUtils;
import org.springframework.util.Assert;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;

public class CaptchaValidateAction extends AbstractAction {
    private ReCaptchaImpl reCaptcha;
    
    public CaptchaValidateAction(ReCaptchaImpl recaptcha) {
        this.reCaptcha = recaptcha;
        Assert.notNull(this.reCaptcha, "ReCaptchaImpl required");
    }
    
    @Override
    protected Event doExecute(RequestContext context) throws Exception {
        HttpServletRequest request = WebUtils.getHttpServletRequest(context);
        String recaptchaChallengeField = request.getParameter("recaptcha_challenge_field");
        String recaptchaResponseField = request.getParameter("recaptcha_response_field");
        String showCaptchaField = request.getParameter("showCaptcha");
        if ((StringUtils.isNotBlank(showCaptchaField)) &&
            !verifyCaptcha(request.getRemoteAddr(), recaptchaChallengeField, recaptchaResponseField)) {
            return error();
        }
        return success();
    }
    
    /**
     * Call recaptcha4j to verify captcha response
     * 
     * @param remoteAddress ip address of the client
     * @param challenge the recaptcha challenge field
     * @param response the recaptcha response field
     * @return boolean indicating verification success or failure
     */
    protected boolean verifyCaptcha(String remoteAddress, String challenge, String response) {        
        ReCaptchaResponse reCaptchaResponse = reCaptcha.checkAnswer(remoteAddress, challenge, response);
        return reCaptchaResponse.isValid();
    }
}
 

Spring Configuration

Add the following file to WEB-INF/spring-configuration:

captcha.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/mvc 
         http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd" >
 
    <description>
      Configuration for reCaptcha
    </description>
  
    <context:annotation-config />
    
	<mvc:annotation-driven />
	<mvc:default-servlet-handler />
 
    <context:property-placeholder location="classpath:/casserver.properties" />
    
    <bean id="reCaptcha" class="net.tanesha.recaptcha.ReCaptchaImpl">
            <property name="privateKey" value="${recaptcha.private.key}" />
            <property name="publicKey" value="${recaptcha.public.key}" />
            <property name="includeNoscript" value="false" />
    </bean>
    
    <bean id="reCaptchaPublicKey" class="java.lang.String">
        <constructor-arg value="${recaptcha.public.key}"/>
    </bean>
        
    <bean id="captchaErrorCountAction" class="com.acme.cas.captcha.CaptchaErrorCountAction"/>
    
    <bean id="captchaValidateAction" class="com.acme.cas.captcha.CaptchaValidateAction">
        <constructor-arg>
            <ref local="reCaptcha"/>
        </constructor-arg>
    </bean>
</beans>

Note the reCaptchaPublicKey is exposed in the context here as a bean for reference in the view.

Add reCaptcha keys to properties file

Add the recaptcha.private.key and recaptcha.public.key properties to src/main/resources/casserver.properties.

casserver.properties
recaptcha.public.key=<your recaptcha public key>
recaptcha.private.key=<your recaptcha private key>

Add reCaptcha to casLoginView.jsp

WEB-INF/view/jsp/default/ui/casLoginView.jsp
<c:if test="${not empty count && count >= 3}">
	<div class="error"><p>Multiple failed login attempts detected!  Please enter the letters shown in the image below to prove you are human.</p></div>
    <div id="captcha"></div>
    <!--[if lte IE 6]>
    <script type="text/javascript" src="https://www.google.com/recaptcha/api/challenge?k=<spring:eval expression="@reCaptchaPublicKey"/>"></script>
    <script type="text/javascript">
    	var isRunningIE6 = true;                              
    </script>
    <![endif]-->
    <script type="text/javascript" src="https://www.google.com/recaptcha/api/js/recaptcha_ajax.js"></script>
    <script>
    	function showCaptcha() {
        	if (typeof(isRunningIE6) === 'undefined') {
            	Recaptcha.create('<spring:eval expression="@reCaptchaPublicKey"/>', "captcha", {
                	theme: 'white',
                    callback: Recaptcha.focus_response_field,
                    tabindex: 2
                });
            }
        }
        showCaptcha();
    </script>                            
    <input type="hidden" name="showCaptcha" value="true" />
</c:if>

Note the reCaptchaPublicKey bean is referenced here.

Add reCaptcha4j dependency in pom.xml

pom.xml
<dependency>
	<groupId>net.tanesha.recaptcha4j</groupId>
    <artifactId>recaptcha4j</artifactId>
    <version>0.0.7</version>
</dependency>
  • No labels