在一個充滿惡意機器人的世界中,您該怎么做才能保護您寶貴的Web應用程序? 您真正應該做的基本事情之一就是向其中添加CAPTCHA功能。 如果您不熟悉(聽起來有些奇怪),則CAPTCHA是確保用戶實際上是真實人物而不是計算機的簡單方法。 這可以通過挑戰用戶并要求他提供對“問題”的響應來完成。 由于計算機無法解決驗證碼,因此假定輸入正確解決方案的任何用戶都是人。 最常見的方法是要求用戶從出現在屏幕上的變形圖像中鍵入字母或數字。
注冊網站時,您很可能已經看到了一個驗證碼文件。 以下是來自維基百科的CAPTCHA示例。
將SimpleCaptcha添加到您的應用程序
在本教程中,我將CAPTCHA功能集成到Web應用程序中。 我將使用SimpeCaptcha框架 ,該框架用于為Java生成CAPTCHA圖像/答案對。 該站點提供了一個安裝指南 ,但是該指南指的是普通的基于JSP的舊應用程序。 我將向您展示如何將該框架與您喜歡的GWT項目集成。
(我將假設您已經在系統上啟動并運行了GWT ,以及用于Eclipse的Google插件 )
首先,讓我們創建我們的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() { @Overridepublic void onSuccess(Boolean result) {if (result) {Window.alert("CAPTCHA was valid");}else {Window.alert("CAPTCHA was invalid");} sendButton.setEnabled(true);} @Overridepublic 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 enabledin 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;@Overridepublic 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-appPUBLIC "-//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
事實是,我已經創建了該項目的新版本,可以在此處獲取(按預期用途)。
請享用!
- 使用Spring Security保護GWT應用程序
- GWT 2 Spring 3 JPA 2 Hibernate 3.5教程– Eclipse和Maven 2展示
- 建立自己的GWT Spring Maven原型
- GWT EJB3 Maven JBoss 5.1集成教程
翻譯自: https://www.javacodegeeks.com/2010/06/add-captcha-gwt-application.html