步骤一:
在servlet容器中部署一个SimpleCaptchaServlet
1、编写一个名为SimpleCaptchaServlet的HttpServlet子类:
//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
<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()方法):
//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():
<result >$ {successResultValue }< /result >
< /action >
Struts负责转发请求,就是访问login.do也就是调用LoginAction类里面的方法login()
步骤三:
编写页面部分,主要是extjs的js文件
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.youkuaiyun.com/Kaiwii
另外,关于SimpleCaptcha的配置,建议你可以阅读官网中的文章:
http://simplecaptcha.sourceforge.net/samples.html
作者:kaiwii