步骤一:

在servlet容器中部署一个SimpleCaptchaServlet

1、编写一个名为SimpleCaptchaServlet的HttpServlet子类:

import java.io.IOException ;
//from kaiwii's blog

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

import nl.captcha.Captcha ;
import nl.captcha.servlet.CaptchaServletUtil ;

public  class SimpleCaptchaServlet extends HttpServlet
{
   private  static final  long serialVersionUID  =  1L ;
   private  static final String PARAM_HEIGHT  =  "height" ;
   private  static final String PARAM_WIDTH  =  "width" ;
   protected  int _width  =  200;
   protected  int _height  =  50;
//from kaiwii's blog

/**
 * 方法通过取默认值或者读取初始化值的方式获取验证码图形的大小
 */

   public  void init ()
    throws ServletException
   {
     if  (getInitParameter ( "height" )  != null )
       this._height  = Integer.valueOf (getInitParameter ( "height" )).intValue ();
     if  (getInitParameter ( "width" )  != null )
       this._width  = Integer.valueOf (getInitParameter ( "width" )).intValue ();
   }

   public  void doGet (HttpServletRequest paramHttpServletRequest , HttpServletResponse paramHttpServletResponse )
    throws ServletException , IOException
   {
//from kaiwii's blog

//根据设置的大小初始化验证码对象localCaptcha
    Captcha localCaptcha  =  new Captcha.Builder ( this._width ,  this._height ).addText ().addBackground ().addNoise ().build ();
//将验证码的图片写到response中
    CaptchaServletUtil.writeImage (paramHttpServletResponse , localCaptcha.getImage ());
//将验证码对象localCaptcha放到Session中,待会会在LoginAction类中取出
    paramHttpServletRequest.getSession ().setAttribute ( "simpleCaptcha" , localCaptcha );
   }
}

2、在web.xml中注册SimpleCaptchaServlet

<!-- 验证码 (form kaiwii’s blog )  -->
<servlet >
         <servlet -name >SimpleCaptcha < /servlet -name >
         <servlet - class >com.htsoft.core.web.servlet.SimpleCaptchaServlet < /servlet - class >
< /servlet >
步骤一总结:

请求路径/CaptchaImg,实际上是请求调用SimpleCaptchaServlet的doGet()方法。每次调用doGet()方法先生成一个SimpleCaptcha对象,(而一个SimpleCaptcha对象又对应一组图片,验证码对),然后将图片作为结果,回应请求,并且将这个SimpleCaptcha对象保存到session中,以便后续步骤:LoginAction类对照验证。


步骤二:

编写LoginAction类作为验证码等信息的校验

1、关于LoginAction类,下面我们只谈谈关于验证码的部分(LoginAction类的login()方法):

//从session中获取Captcha类对象localCaptcha;localCaptcha中包含了原来的一组图片,验证码对
//from kaiwii's blog

    Captcha localCaptcha  =  (Captcha )getSession ().getAttribute ( "simpleCaptcha" );
               if  (localCaptcha  == null )
                localStringBuffer.append ( "请刷新验证码再登录.'" );
               else  if  (localCaptcha.isCorrect ( this.checkCode )) //检验验证码对是否正确
               

2、在struts.xml中注册LoginAction类和LoginAction类里面的方法login():

<action name = "login"  class = "LoginAction" method = "login" >
     <result >$ {successResultValue }< /result >
< /action >
步骤二总结:

Struts负责转发请求,就是访问login.do也就是调用LoginAction类里面的方法login()


步骤三:

编写页面部分,主要是extjs的js文件

//from kaiwii's blog
Ext.ns ( "App" );
App.LoginWin  = function (b , g )  { //b,g的值通过login.jsp传递过来
     this.isCodeEnabled  = b ;
     this.isDyPwdEnabled  = g ;
     //输入框群组的布局
    var a  =  new Ext.form.FormPanel ({
        id  :  "LoginFormPanel" ,
        bodyStyle  :  "padding-top:6px" ,
        defaultType  :  "textfield" ,
        columnWidth  :  0. 75,
        labelAlign  :  "right" ,
        labelWidth  :  55,
        labelPad  :  0,
        border  :  false ,
        layout  :  "form" ,
        defaults  :  {
            style  :  "margin:0 0 0 0" ,
            anchor  :  "90%,120%" ,
            selectOnFocus  :  true
         },
        items  :  [  {
            name  :  "username" ,
            fieldLabel  :  "账      号" ,
            cls  :  "text-user" ,
            allowBlank  :  false ,
            blankText  :  "账号不能为空"
         },  {
            name  :  "password" ,
            fieldLabel  :  "密      码" ,
            allowBlank  :  false ,
            blankText  :  "密码不能为空" ,
            cls  :  "text-lock" ,
            inputType  :  "password"
         },  {
            name  :  "checkCode" ,
            id  :  "checkCode" ,
            fieldLabel  :  "验证码" ,
            allowBlank  :  false ,
            hidden  :  true ,
            cls  :  "text-code" ,
            blankText  :  "验证码不能为空"
         },  {
            name  :  "curDynamicPwd" ,
            hidden  :  true ,
            id  :  "curDynamicPwd" ,
            fieldLabel  :  "令     牌" ,
            cls  :  "text-dynamic" ,
            blankText  :  "令牌不能为空"
         },  { //只是一个容器,里面到底是什么,需要另外指定
             //在下面指定
             /**
             * var f = Ext.getCmp("codeContainer");
             *  f.add(d);
             */

            xtype  :  "container" ,
            id  :  "codeContainer" ,
            layout  :  "table" ,
            defaultType  :  "textfield" ,
            hideLabel  :  false ,
            layoutConfig  :  {
                columns  :  3
             }
         },  {
            xtype  :  "container" ,
            style  :  "padding-left:57px" ,
            layout  :  "column" ,
            items  :  [  {
                xtype  :  "checkbox" ,
                name  :  "_spring_security_remember_me" ,
                boxLabel  :  "让系统记住我 "
             },  {
                xtype  :  "panel" ,
                border  :  false ,
                bodyStyle  :  "font-size:12px;padding-left:80px;" ,
                html  :  '<a href="javascript:toSuggestBox()">意见箱</a>'
             }  ]
         }  ]
     });
     if  ( this.isCodeEnabled  !=  "undefined"  &&  this.isCodeEnabled  !=  ""
             &&  this.isCodeEnabled  !=  "1"  ||  this.isCodeEnabled  ==  "close" )  {
        a.remove (Ext.getCmp ( "checkCode" ));
     }  else  {
        Ext.getCmp ( "checkCode" ).show ();
        var d  =  [
                 {
                    width  :  55,
                    xtype  :  "label" ,
                    text  :  "    "
                 },
                 {
                    width  :  150,
                    id  :  "loginCode" ,
                    xtype  :  "panel" ,
                    border  :  false ,
                     //这句是验证码框架simplecaptcha的一个关键语句
                     //通过以下的url请求资源,servlet容器将其请求的资源解释
                     //为请求SimpleCaptchaServlet,而最终调用里面的doGet()方法
                     //doGet()方法会生成一个simpleVaptcha对象,这个simplecaptcha对象
                     //将会回应一个图片
                    html  :  '<img border="0" height="30" width="150" src="'
                             + __ctxPath  +  '/CaptchaImg"/>'
                 },  {
                    width  :  55,
                    xtype  :  "panel" ,
                    border  :  false ,
                    bodyStyle  :  "font-size:12px;padding-left:12px" ,
                     //作为一个链接,请求刷新验证码的方法:refeshCode()
                    html  :  '<a href="javascript:refeshCode()">看不清</a>'
                 }  ];
        var f  = Ext.getCmp ( "codeContainer" );
         //codeContainer原来只是一个空的容器,现在将d的内容赋进去
        f.add (d );
        f.doLayout ();
     }
     if  ( this.isDyPwdEnabled  !=  "undefined"  &&  this.isDyPwdEnabled  !=  ""
             &&  this.isDyPwdEnabled  !=  "1"  ||  this.isDyPwdEnabled  ==  "close" )  {
        a.remove (Ext.getCmp ( "curDynamicPwd" ));
     }  else  {
        Ext.getCmp ( "curDynamicPwd" ).show ();
     }
    var e  = function ()  {
         if  (a.form.isValid ())  {
            a.form.submit ({
                waitTitle  :  "请稍候" ,
                waitMsg  :  "正在登录......" ,
                 //请求url:/login.do,struts框架会将其输入框里面的值都
                 //转发到loginaction类里面,并且调用loginaction类里面
                 //login()方法
                url  : __ctxPath  +  "/login.do" ,
                success  : function (h , i )  {
                    handleLoginResult (i.result );
                 },
                failure  : function (h , i )  {
                    handleLoginResult (i.result );
                    h.findField ( "password" ).setRawValue ( "" );
                    h.findField ( "username" ).focus ( true );
                 }
             });
         }
     };
     //这个是整个登录页面的布局
    var c  =  new Ext.Window ({
        id  :  "LoginWin" ,
        title  :  "用户登录" ,
        iconCls  :  "login-icon" ,
        bodyStyle  :  "background-color: white" ,
        border  :  true ,
        closable  :  false ,
        resizable  :  false ,
        buttonAlign  :  "center" ,
        height  :  275,
        width  :  460,
        layout  :  {
            type  :  "vbox" ,
            align  :  "stretch"
         },
        keys  :  {
            key  : Ext.EventObject.ENTER ,
            fn  : e ,
            scope  :  this
         },
        items  :  [  {
            xtype  :  "panel" ,
            border  :  false ,
            bodyStyle  :  "padding-left:60px" ,
            html  :  '<img src="'  + __loginImage  +  '" />' ,
            height  :  50
         },  {
            xtype  :  "panel" ,
            border  :  false ,
            layout  :  "column" ,
            items  :  [ a ,  {
                xtype  :  "panel" ,
                border  :  false ,
                columnWidth  :  0. 25,
                html  :  '<img src="'  + __ctxPath  +  '/images/login-user.jpg"/>'
             }  ]
         }  ],
        buttons  :  [  {
            text  :  "登录" ,
            iconCls  :  "btn-login" ,
            handler  : e.createDelegate ( this )
         },  {
            text  :  "重置" ,
            iconCls  :  "btn-login-reset" ,
            handler  : function ()  {
                a.getForm ().reset ();
             }
         }  ]
     });
     return c ;
};
function handleLoginResult (a )  {
     if  (a.success )  {
        Ext.getCmp ( "LoginWin" ).hide ();
        var b  =  new Ext.ProgressBar ({
            text  :  "正在登录..."
         });
        b.show ();
        window.location.href  = __ctxPath  +  "/index.jsp" ;
     }  else  {
        Ext.MessageBox.show ({
            title  :  "操作信息" ,
            msg  : a.msg ,
            buttons  : Ext.MessageBox.OK ,
            icon  : Ext.MessageBox.ERROR
         });
     }
}
//刷新验证码对象,并且更新验证码容器里面的内容
//from kaiwii's blog
//关键:
function refeshCode ()  {
    var a  = Ext.getCmp ( "loginCode" );
    a.body.update ( '<img border="0" height="30" width="150" src="'  + __ctxPath
             +  "/CaptchaImg?rand="  + Math.random ()  +  '"/>' );
}
function toSuggestBox ()  {
    window.open (__ctxPath  +  "/info/suggest.do" ,  "_blank" );
代码中已经含有详细分析,所以关于步骤三的总结就不再累赘了!


机制分析:

servlet容器启动伊始,读取web.xml文件,发现web.xml中已经注册了SimpleCaptchaServlet,所以调用SimpleCaptchaServlet的init()方法,初始化SimpleCaptcha的大小。

用户打开login页面,在初始化页面的过程中,会读取js文件中的内容。其中,为了初始化验证码图片的时候,会请求得到一个图片资源。请求发送到服务器端的时候,servlet

容器会根据web.xml中的url pattern将这个请求理解成请求SimpleCaptchaServlet,并且调用了SimpleCaptchaServlet里面的doGet()方法。每次调用doGet()方法,都会生成一个SimpleCaptcha对象,而一个SimpleCaptcha对象里面就含有一组图片和验证码信息(在SimpleCaptcha的机制中,用户是根据图片的信息与验证码的信息作匹配来达到验证的目的的)。所以,SimpleCaptchaServlet会将SimpleCaptcha对象中的那个图片作为结果回应这个url请求,并且将这个SimpleCaptcha对象保存到session对象中,以便后续步骤中的其他类使用。这个时候,用户的客户端页面就可以获得了一个图片资源,并且使用这个图片资源来初始化验证码页面。

当用户填写完验证码和用户名、密码之后,点击按钮,触发页面的js方法。这个页面的js方法,会请求login.do资源。当这个请求发送到服务器端的时候,struts框架会根据struts.xml文件,将这个请求转发到LoginAction类中,并且将页面中由验证码和用户名、密码组成的表单(form)作为参数调用LoginAction类的login()方法。login()方法会从session对象中取出前面步骤中生成的SimpleCaptcha对象。并且使用这个SimpleCaptcha对象验证用户发送过来的验证码是否正确。


结束语:

本文只是谈了一下,基于extjs+servlet+struts+SimpleCaptcha框架下,如何实现验证码校验的基本设计和实施。至于如何配置SimpleCaptcha,extjs,struts等,请参考本博客中的其他文章:http://blog.csdn.net/Kaiwii

另外,关于SimpleCaptcha的配置,建议你可以阅读官网中的文章:

http://simplecaptcha.sourceforge.net/samples.html


                                                 作者:kaiwii







Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐