将CAPTCHA添加到您的GWT应用程序

什么是验证码?
在一个充满恶意机器人的世界中,您该怎么做才能保护您宝贵的Web应用程序? 您真正应该做的基本事情之一就是向其中添加CAPTCHA功能。 如果您不熟悉(听起来有些奇怪),则CAPTCHA是确保用户实际上是真实人物而不是计算机的简单方法。 这可以通过挑战用户并要求他提供对“问题”的响应来完成。 由于计算机无法解决验证码,因此假定输入正确解决方案的任何用户都是人。 最常见的方法是要求用户从出现在屏幕上的变形图像中键入字母或数字。

注册网站时,您很可能已经看到了一个验证码文件。 以下是来自维基百科的CAPTCHA示例。


将SimpleCaptcha添加到您的应用程序
在本教程中,我将CAPTCHA功能集成到Web应用程序中。 我将使用SimpeCaptcha框架 ,该框架用于为Java生成CAPTCHA图像/答案对。 该站点提供了一个安装指南 ,但是该指南指的是普通的基于JSP的旧应用程序。 我将向您展示如何将该框架与您喜欢的GWT项目集成。

(我将假设您已经在系统上启动并运行GWT ,以及用于EclipseGoogle插件

首先,让我们创建我们的Eclipse项目。 选择文件 ? Web应用程序项目”,并提供必要的信息,如下图所示。 该项目的名称将为“ CaptchaGwtProject”。 确保包括对GWT的支持,但不包括Google App Engine。

在继续之前,请注意此处。 SimpleCaptcha大量使用AWT类来执行图像渲染。 但是,您可能知道,App Engine不支持所有JRE类,最具体地说,其中仅包含AWT包中的几个。 您可以查看AppEngine JRE类白名单以了解更多详细信息。 因此,不可能将框架合并到将在App Engine上部署的应用程序中,并且仅应将其与在标准JRE上运行的平台一起使用。

下一步是从SourceForge下载该库(我使用1.1.1版本 )。 将下载的JAR文件添加到项目的类路径中。 另外,不要忘记将JAR文件复制到“ CaptchaGwtProject \ war \ WEB-INF \ lib”文件夹,因为嵌入式容器的(Jetty)运行时将需要该文件。 您可以在此处找到该项目的JavaDoc页面。

设置客户端
Eclipse将自动创建应用程序的框架,并创建一些示例文件。 找到CaptchaGwtProject类,它是应用程序的入口点。 删除现有内容,并将其替换为以下内容:

package com.javacodegeeks.captcha.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;

public class CaptchaGwtProject implements EntryPoint {

    private final SignupServiceAsync signupService = GWT.create(SignupService.class);
    
    private final Button sendButton = new Button("Sign Up");
    
    public void onModuleLoad() {
        
        final TextBox usernameField = new TextBox();
        usernameField.setText("Username here");
        
        final TextBox passwordField = new TextBox();
        passwordField.setText("Password here");

        final TextBox captchaField = new TextBox();
        captchaField.setText("CAPTCHA Word here");
        
        final Label responseLabel = new Label();        
        final Image captchaImage = new Image("/SimpleCaptcha.jpg");

        usernameField.setFocus(true);
        sendButton.addStyleName("sendButton");
        
        RootPanel.get("usernameFieldContainer").add(usernameField);
        RootPanel.get("passwordFieldContainer").add(passwordField);
        RootPanel.get("captchaFieldContainer").add(captchaField);
        RootPanel.get("sendButtonContainer").add(sendButton);
        RootPanel.get("captchaImageContainer").add(captchaImage);
        RootPanel.get("responseLabelContainer").add(responseLabel);

        class MyHandler implements ClickHandler, KeyUpHandler {
            public void onClick(ClickEvent event) {
                sendDataToServer();
            }
            public void onKeyUp(KeyUpEvent event) {
                if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
                    sendDataToServer();
                }
            }
            private void sendDataToServer() {                
                String username = usernameField.getText();
                String password = passwordField.getText();
                String captcha = captchaField.getText();
                sendButton.setEnabled(false);    
                signupService.performSignup(username, password, captcha, signupCallback);                
            }
        }

        MyHandler handler = new MyHandler();
        sendButton.addClickHandler(handler);
        usernameField.addKeyUpHandler(handler);
        
    }

    private AsyncCallback signupCallback = new AsyncCallback() {        
        @Override
        public void onSuccess(Boolean result) {
            if (result) {
                Window.alert("CAPTCHA was valid");
            }
            else {
                Window.alert("CAPTCHA was invalid");
            }            
            sendButton.setEnabled(true);
        }        
        @Override
        public void onFailure(Throwable caught) {
            Window.alert("Error occurred while communicating with server");
            sendButton.setEnabled(true);
        }        
    };
    
}

该代码简单明了,并且基于自动创建的示例类。 我们为用户输入添加了两个TextBox ,为服务器响应添加了Label 。 我们还添加了一个Image实例,它将作为我们的验证码的占位符。 我们将其URL设置为“ /SimpleCaptcha.jpg”,该URL将由框架处理。 最后,使用一个Button来调用对服务器的调用。 AsyncCallback使用布尔值来表示服务器上验证码验证失败的成功。 请注意,故意遗漏了Web应用程序的某些基本部分(例如用户输入验证),因此我们可以专注于CAPTCHA部分。

接下来,在项目的“ war”文件中找到名为“ CaptchaGwtProject.html”HTML文件。 编辑文件并为我们的GWT对象添加一些容器。 代码如下:

<!doctype html>
<!-- The DOCTYPE declaration above will set the    -->
<!-- browser's rendering engine into               -->
<!-- "Standards Mode". Replacing this declaration  -->
<!-- with a "Quirks Mode" doctype may lead to some -->
<!-- differences in layout.                        -->

<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">

    <!--                                                               -->
    <!-- Consider inlining CSS to reduce the number of requested files -->
    <!--                                                               -->
    <link type="text/css" rel="stylesheet" href="CaptchaGwtProject.css">

    <!--                                           -->
    <!-- Any title is fine                         -->
    <!--                                           -->
    <title>Web Application Starter Project</title>
    
    <!--                                           -->
    <!-- This script loads your compiled module.   -->
    <!-- If you add any GWT meta tags, they must   -->
    <!-- be added before this line.                -->
    <!--                                           -->
    <script type="text/javascript" language="javascript" src="captchagwtproject/captchagwtproject.nocache.js"></script>
  </head>

  <!--                                           -->
  <!-- The body can have arbitrary html, or      -->
  <!-- you can leave the body empty if you want  -->
  <!-- to create a completely dynamic UI.        -->
  <!--                                           -->
  <body>

    <!-- OPTIONAL: include this if you want history support -->
    <iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>
    
    <!-- RECOMMENDED if your web app will not function without JavaScript enabled -->
    <noscript>
      <div style="width: 22em; position: absolute; left: 50%; margin-left: -11em; 
      color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif">
        Your web browser must have JavaScript enabled
        in order for this application to display correctly.
      </div>
    </noscript>

    <h1>CAPTCHA Secured Web Application</h1>

    <table align="center">
      <tr>
        <td colspan="2" style="font-weight:bold;">Please enter your username:</td>
        <td id="usernameFieldContainer"></td>
      </tr>
      <tr>
        <td colspan="2" style="font-weight:bold;">Please enter your password:</td>
        <td id="passwordFieldContainer"></td>
      </tr>
      <tr>
        <td colspan="2" style="font-weight:bold;">Please enter the word:</td>
        <td id="captchaFieldContainer"></td>
      </tr>
      <tr>
        <td id="sendButtonContainer"></td> 
      </tr>
      <tr>
        <td id="captchaImageContainer"></td> 
      </tr>
      <tr>
        <td colspan="2" style="color:red;" id="responseLabelContainer"></td>
      </tr>
    </table>
            
  </body>
</html>

请注意,对自动生成的文件的唯一更改是在<h1>标记之后。

我们的异步GWT服务将非常简单,仅执行一项功能。 相应的两个接口如下所示:

package com.javacodegeeks.captcha.client;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath("signup")
public interface SignupService extends RemoteService {
 
    boolean performSignup(String username, String password, String userCaptcha);
 
}
package com.javacodegeeks.captcha.client;

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface SignupServiceAsync {
 
    void performSignup(String username, String password, String userCaptcha,
        AsyncCallback callback);
 
}

(请注意,自动生成的“ greetingService”类已被删除)

准备服务器端
在服务器端,我们从库中使用的主要对象是Captcha 。 要检索验证码的值(并将其与用户输入进行比较),我们必须获得对与特定会话关联的HttpSession对象的引用。 可以通过相应的HttpServletRequest对象检索HttpSession。 这是标准的Java EE内容。 不要忘记服务器端GWT服务继承自RemoteServiceServlet ,而RemoteServiceServlet继承自HttpServletRequest。 可以通过调用getThreadLocalRequest方法获得基础请求。 请注意,正如API所提到的,这是本地存储在线程中的,因此同时调用可以具有不同的请求对象。

服务器端的具体实现如下:

package com.javacodegeeks.captcha.server;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import nl.captcha.Captcha;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.javacodegeeks.captcha.client.SignupService;

@SuppressWarnings("serial")
public class SignupServiceImpl extends RemoteServiceServlet implements SignupService {

    public boolean performSignup(String username, String password, String userCaptcha) {
        
        HttpServletRequest request = getThreadLocalRequest();
        
        HttpSession session = request.getSession();

        Captcha captcha = (Captcha) session.getAttribute(Captcha.NAME);
        
        return captcha.isCorrect(userCaptcha);
        
    }
}

扩展SimpleCaptcha
最后一步是设置Servlet,该Servlet将生成显示给用户的图像。 通过创建从提供的SimpeCaptchaServlet类继承的类,可以轻松扩展SimpleCaptcha。 相关代码如下:

package com.javacodegeeks.captcha.server.servlet;

import static nl.captcha.Captcha.NAME;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import nl.captcha.Captcha;
import nl.captcha.backgrounds.GradiatedBackgroundProducer;
import nl.captcha.servlet.CaptchaServletUtil;
import nl.captcha.servlet.SimpleCaptchaServlet;

public class ExtendedCaptchaServlet extends SimpleCaptchaServlet {
    
    private static final long serialVersionUID = 6560171562324177699L;
     
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        
        HttpSession session = req.getSession();
        
        Captcha captcha = new Captcha.Builder(_width, _height)
        .addText()
        .addBackground(new GradiatedBackgroundProducer())
        .gimp()
        .addNoise()
        .addBorder()
        .build();

        session.setAttribute(NAME, captcha);
        CaptchaServletUtil.writeImage(resp, captcha.getImage());
        
    }
    
}

作为初始化参数传递并从父类读取的“ _width”和“ _height”变量。 要创建一个新的Captcha对象,我们使用Captcha.Builder类(依赖于构建器模式)。 然后,我们将对象传递到特定的会话,并将关联的BufferedImage流传输到servlet响应。

请注意,我们的实现每次用户执行页面请求时都会生成一个新图像。 这与在给定会话中使用相同图像的默认SimpleCaptcha实现不同。

配置Web应用程序
所有组件都通过Web应用程序的“ web.xml”描述符捆绑在一起,在我们的示例中,描述符如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
  
  <!-- Default page to serve -->
  <welcome-file-list>
    <welcome-file>CaptchaGwtProject.html</welcome-file>
  </welcome-file-list>
  
  <!-- Servlets -->
  <servlet>
    <servlet-name>signupServlet</servlet-name>
    <servlet-class>
 com.javacodegeeks.captcha.server.SignupServiceImpl
    </servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>signupServlet</servlet-name>
    <url-pattern>/captchagwtproject/signup</url-pattern>
  </servlet-mapping>
  
  <servlet>
    <servlet-name>SimpleCaptcha</servlet-name>
    <servlet-class>
 com.javacodegeeks.captcha.server.servlet.ExtendedCaptchaServlet
    </servlet-class>
    <init-param>
        <param-name>width</param-name>
        <param-value>200</param-value>
    </init-param>
    <init-param>
        <param-name>height</param-name>
        <param-value>50</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>SimpleCaptcha</servlet-name>
    <url-pattern>/SimpleCaptcha.jpg</url-pattern>
  </servlet-mapping>

</web-app>

我们声明GWT服务(类“ SignupServiceImpl”)和欢迎文件。 这里没什么特别的。 最后,我们声明将负责图像生成并在“'/SimpleCaptcha.jpg”URL下处理请求的servlet(请记住,此请求在GWT入口点中使用)。 我们还提供了servlet的初始化参数(宽度和高度)。

而已! 运行该项目,您应该看到类似以下内容的内容:

每次刷新页面时,都会创建一个新的CAPTCHA映像。 单击“注册”按钮后,字段内容将发送到服务器,在服务器上用户提供的CAPTCHA值将根据当前会话中存在的值进行测试。 如果发生故障,我们将看到:


而已。 现在,您的应用程序可以更安全一些。

您可以在此处找到完整的Eclipse项目。

更新:我们的一位读者询问是否可以添加“重新加载图像”功能。 确实有可能,但是从服务器重新获取图像时必须小心。 如以下讨论所述,浏览器将不会重新获取图像(因为它具有相同的URL)。 诀窍是添加一个虚拟参数,该参数将在每次刷新时更改。 在这里看看文章:

http://groups.google.gy/group/google-web-toolkit/browse_thread/thread/be9f1da56b5b1c18

事实是,我已经创建了该项目的新版本,可以在此处获取(按预期用途)。

请享用!

相关文章 :

翻译自: https://www.javacodegeeks.com/2010/06/add-captcha-gwt-application.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值