步骤一:
在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.csdn.net/Kaiwii
另外,关于SimpleCaptcha的配置,建议你可以阅读官网中的文章:
http://simplecaptcha.sourceforge.net/samples.html
作者:kaiwii