JSF Login Form CAPTCHA Code Example

The JSF Login Form Captcha code example shows how to add BotDetect CAPTCHA validation to basic Login form filter in JSF Web Applications.

To prevent bots from trying to guess the login info by brute force submission of a large number of common values, the visitor first has to prove they are human (by solving the CAPTCHA), and only then is their username and password submission checked against the authentication data store.

Also, if they enter an invalid username + password combination three times, they have to solve the CAPTCHA again. This prevents attempts in which the attacker would first solve the CAPTCHA themselves, and then let a bot brute-force the authentication info.

To keep the example code simple, the example doesn't access a data store to authenticate the user, but accepts all logins with usernames and passwords at least 5 characters long as valid.

Download the BotDetect Java CAPTCHA Generator archive to run this example

JSF 2.0+

Within this page, the root folder of the extracted archive is referred as the <BDC-DIR>.

This example is in the <BDC-DIR>/examples/t_api-captcha-jsf2.0-login-form/ folder; and contains the following files:

Running Example

This example's war file (in BotDetect download package) already embeds all dependencies.

In case you are making this example from scratch in your IDE, you need to ensure botdetect.jar, botdetect-servlet.jar, botdetect-jsp20.jar, and botdetect-jsf20.jar are in the classpath.

login.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" 
   xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" 
   xmlns:botDetect="https://captcha.com/java/jsf">
  
  <h:head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>BotDetect Java CAPTCHA Validation: JSF Login Form Filter CAPTCHA Code Example</title>
    <h:outputStylesheet library="css" name="stylesheet.css"  />
  </h:head>
  
  <h:body>
    <h:form styleClass="column">
      <h1>BotDetect Java CAPTCHA Validation:<br/> JSF Login Form Filter CAPTCHA Code Example</h1>
      <fieldset>
        <legend>
          CAPTCHA included in JSF Login form filter validation
        </legend>

        <h:panelGrid columns="1">
          <h:outputText value="Username:"/>
          <h:inputText id="username" value="#{loginForm.username}"/>

          <h:outputText value="Password:"/>
          <h:inputSecret id="password" value="#{loginForm.password}"/>

          <h:panelGroup>
            <h:outputLabel for="captchaCode" value="Retype the characters from the picture:"/>
            
            <!-- Adding BotDetect Captcha to the page -->
            <botDetect:jsfCaptcha id="loginFormFilter" 
                      userInputID="captchaCode"
                      imageWidth="200"
                      codeLength="4"
                      binding="#{loginForm.captcha}"/>

            <div class="validationDiv">
              <h:inputText id="captchaCode" value="#{loginForm.captchaCode}"/>
            </div>
          </h:panelGroup>

          <h:outputText value="#{loginForm.errorMessage}" styleClass="incorrect"/>
          <h:commandButton action="#{loginForm.login}" value="Login"/>
           
        </h:panelGrid>
      </fieldset>
    </h:form>
  </h:body>
</html>

The example login form is simple, containing textboxes for username and password input. We add Captcha protection to the form using the standard procedure.

LoginForm.java

package com.captcha.botdetect.examples.jsf.login_form_filter.view;

import com.captcha.botdetect.web.jsf.JsfCaptcha;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean(name = "loginForm")
@SessionScoped
public class LoginForm {

  private String username;
  private String password;
  private String captchaCode;
  private JsfCaptcha captcha;
  private String errorMessage;
  private boolean isLoggedIn;

  /**
   * Login operation.
   *
   * @return
   */
  public String login() {
    // validate captcha and user 
    if (!validateCaptcha() || !validateLogin()) {
      // Captcha or user validation failed
      return "";
    }

    //Check user and captcha passed
    // TODO
    isLoggedIn = true;
    return "secured/index?faces-redirect=true";
  }

  /**
   * Logout operation.
   *
   * @return
   */
  public String logout() {
    username = null;
    captchaCode = null;
    errorMessage = null;
    isLoggedIn = false;
    
    return "/login";
  }

  private boolean validateCaptcha() {
    // validate the Captcha to check we're not dealing with a bot
    if (!this.captcha.validate(captchaCode)) {
      // Captcha validation failed, show error message
      errorMessage = "Invalid code";
      
      // reset captcha code textbox value
      captchaCode = null;
    
      return false;
    }
    
    // Captcha validation passed, perform protected action
    // TODO
    return true;
  }

  //Set connection database user and validate here
  private boolean validateLogin() {
    String pattern = "^[a-zA-Z0-9_]+$"; // alphanumeric chars and underscores only

    if (!username.matches(pattern) || !password.matches(pattern)) {
      errorMessage = "Invalid authentication info";
      return false;
    }
    
    if ((username.length() < 5) || (password.length() < 5)) {
      errorMessage = "Authentication failed";
      return false;
    }
     
    return true;
  }

  // Getters & Setters 
  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  public String getPassword() {
    return password;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  public String getCaptchaCode() {
    return captchaCode;
  }

  public void setCaptchaCode(String captchaCode) {
    this.captchaCode = captchaCode;
  }

  public JsfCaptcha getCaptcha() {
    return captcha;
  }

  public void setCaptcha(JsfCaptcha captcha) {
    this.captcha = captcha;
  }

  public String getErrorMessage() {
    return errorMessage;
  }

  public void setErrorMessage(String errorMessage) {
    this.errorMessage = errorMessage;
  }

  public boolean isLoggedIn() {
    return isLoggedIn;
  }

  public void setIsLoggedIn(boolean isLoggedIn) {
    this.isLoggedIn = isLoggedIn;
  }
}

In the code above, we perform simple login authentication and perform Captcha validation in backing bean's form action method.

As previously mentioned, the actual authentication code is not implemented, but the example code should demonstrate the underlying principles adequately.

LoginFilter.java

package captcha.com.botdetect.examples.jsf.login_form_filter.filters;

import captcha.com.botdetect.examples.jsf.login_form_filter.view.LoginForm;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginFilter implements Filter {

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    // Get the loginBean from session attribute
    LoginForm loginForm = (LoginForm) ((HttpServletRequest) request).getSession().getAttribute("loginForm");

    // Redirect to the login.xhtml page if user is not logged in.
    if ((loginForm == null) || (!loginForm.isLoggedIn())) {
      String contextPath = ((HttpServletRequest) request).getContextPath();
      ((HttpServletResponse) response).sendRedirect(contextPath + "/login.xhtml");
    }

    chain.doFilter(request, response);
  }

  @Override
  public void init(FilterConfig config) throws ServletException {
  }

  @Override
  public void destroy() {
  }        
}

In LoginFilter, which secures all pages in the secured directory in your web application and redirects to login page if user is not logged in.

secured/index.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
   xmlns:h="http://java.sun.com/jsf/html">
  
  <h:head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>BotDetect Java CAPTCHA Validation: JSF Login Form Filter CAPTCHA Code Example</title>
    <h:outputStylesheet library="css" name="stylesheet.css"  />
  </h:head>
  
  <h:body>
    <h:form styleClass="column">
      <h1>BotDetect Java CAPTCHA Validation:<br/> JSF Login Form Filter CAPTCHA Code Example</h1>
      <h2>View Messages</h2>
      <p class="message">Here is the protected page use Login form filter, this means that you are logged in successfully with a valid username, password, and captcha code.</p>
      
      <div><h:commandButton action="#{loginForm.logout}" value="Logout"/></div>
    </h:form>
  </h:body>
</html>

This page is secured by LoginFilter, we simply add some messages that let user know that user is logged in successfully.

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
     xmlns="http://java.sun.com/xml/ns/javaee" 
     xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
     id="WebApp_ID" >
  
  <context-param>
    <param-name>javax.faces.PROJECT_STAGE</param-name>
    <param-value>Development</param-value>
  </context-param>

  <context-param>
    <param-name>javax.faces.VALIDATE_EMPTY_FIELDS</param-name>
    <param-value>true</param-value>
  </context-param>

  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/faces/*</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.faces</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.jsf</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
  </servlet-mapping>
  
  <servlet>
    <servlet-name>BotDetect Captcha</servlet-name>
    <servlet-class>com.captcha.botdetect.web.servlet.CaptchaServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>BotDetect Captcha</servlet-name>
    <url-pattern>/botdetectcaptcha</url-pattern>
  </servlet-mapping>

  <filter>
    <filter-name>LoginFilter</filter-name>
    <filter-class>com.captcha.botdetect.examples.jsf.login_form_filter.filters.LoginFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>LoginFilter</filter-name>
    <url-pattern>/secured/*</url-pattern>
  </filter-mapping>
  
  <welcome-file-list>
    <welcome-file>secured/index.xhtml</welcome-file>
  </welcome-file-list>
</web-app>

In WEB-INF/web.xml file we register CaptchaServlet used for BotDetect Captcha requests and register LoginFilter used for securing all pages in the secured directory.

JSF 1.2

The JSF 1.2 Login Form Filter CAPTCHA code example is included in the
examples/java-t_api-captcha-jsf2.0-login-form/ folder of the download package.


Please Note

BotDetect Java Captcha Library v4.0.Beta3.7 is an in-progress port of BotDetect 4 Captcha, and we need you to guide our efforts towards a polished product. Please let us know if you encounter any bugs, implementation issues, or a usage scenario you would like to discuss.