Java通用验证码程序及应用示例(提供源码下载)

把之前项目中写的一个Java通用验证码程序整理了一下,与大家分享,希望能抛砖引玉。

一、主要功能:

1、支持纯数字、大写字母、小写字母,及两两混合或三者混合类型验证码;

2、支持自定义特殊字符排除(如0oOi1jI);

3、支持图片及文字两种类型验证码;

4、支持自定义验证码图片大小;

5、支持自定义干扰线条数;

6、支持自定义及随机定义图片、文字、干扰线颜色;


其他:示例页面提供了<iframe>和<img>两种页面显示验证码的方式。


a、仅阿拉伯数字

b、仅字母(大小写混合)

c、仅小写字母

d、仅大写字母

d、数字与小写字母混合

e、数字与大写字母混合

f、数字与大小写字母混合

g、自定义字符颜色(或随机)

h、自定义图片颜色(或随机)

i、自定义干扰线的条数及颜色(或随机)

j、自定义验证码图片大小

k、自定义需排除的特殊字符(如0o等难分辨字符)


二、代码说明:

1、pubMsg.jsp 发表留言页面,实现<iframe>和<img>两种显示验证码方式,及ajax提交功能


<%@ page language="java" import="java.util.*" pageEncoding="gbk"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  
<head>
    
<title><%=basePath%></title>
  
</head>

  
<script type="text/javascript">
  
  
var req;
  
  
//提交留言
  function pubMsg(){

      msgTitle
=document.msgForm.msgTitle.value;
      msgContent
=document.msgForm.msgContent.value;
      verifyCode
=document.msgForm.verifyCode.value;
      
      
//检查输入内容格式
      if(msgTitle.length<5||msgContent.length<5)
      {
          alert(
"标题和内容不能少于5个字符");
          
return;
      }
    
if(verifyCode.length==0){
        alert(
"请输入验证码");
        
return;
    }
    
    
//获得XMLHttpRequest对象
    if(window.XMLHttpRequest)
        req
=new XMLHttpRequest();
    
else if(window.ActiveXObject)
        req
=new ActiveXObject("Microsoft.XMLHTTP");
    
    
//提交请求
    if(req){
        req.onreadystatechange
=callBack;//指定服务器响应结果处理函数(注意仅函数名无括号)
        url="<%=path%>/servlet/PubMsgServlet?msgTitle="+msgTitle+"&msgContent="+msgContent+"&verifyCode="+verifyCode;
        req.open(
"GET",url,false);
        req.send();
        
    }
  
  }
  
  
//服务器响应结果处理函数
  function callBack(){

      
if(req.readyState==4){
          
if(req.status==200){
              next
=req.responseText;//获得服务器处理结果
              if(next=="this"){
                  alert(
"验证码不正确,请重新输入");
                  verifyCodeFrame.location.reload();
//刷新验证码
              }
              
else{
                  document.msgForm.msgTitle.value
="";
                  document.msgForm.msgContent.value
="";
                  window.location
=next;//跳转页面
              }
          }
else
          {
              alert(req.status
+":"+req.statusText);
          }
      }
      document.msgForm.verifyCode.value
="";
  }
  
  
//更换验证码
  function changeVerifyCode(){
    
    
//1、如果用<iframe>实现,则重新加载<iframe>的内容
      //verifyCodeFrame.location.reload();
      
      
//2、如果用<img>实现,则修改<img src=url>的url
      //这里有一个小技巧,如果给url赋相同的值,浏览器不会重新发出请求,因此用js生成一个即时毫秒数做url中的参数
      t=new Date().getTime();
      document.msgForm.verifyCodeImg.src
="<%=path%>/servlet/VerifyCodeServlet?t="+t;
  }
   
  
</script>
  
  
<body>
  
  
<form name="msgForm" action="" method="post">
  
<table border=0>
  
<tr><td>标题:</td><td><input name="msgTitle" size="65"/></td></tr>
  
<tr><td>内容:</td><td><textarea name="msgContent" rows="10" cols="64"></textarea></td></tr>
  
<tr><td>验证码:</td><td><input name="verifyCode" style="height:29px;width=70px;font-size:25px"/>
  
  
<!-- 以下:两种显示验证码的方式 -->
  
  
<!-- 1、采用<iframe>实现 -->
  
<!--
  <iframe name="verifyCodeFrame" src="<%=path+"/servlet/VerifyCodeServlet"%>" width="100" height="30" frameborder=0 align="top" marginheight=0 marginwidth=0 scrolling=no></iframe>
   
-->
  
<!-- 2、采用<img>实现 -->
  
<img name="verifyCodeImg" src="<%=path%>/servlet/VerifyCodeServlet" style="cursor:hand" align="top" onClick="changeVerifyCode()"/>
  
  
<!-- 以上:两种显示验证码的方式 -->
  
  
  
<font style="color:blue;font-size=14"><href="javascript:changeVerifyCode()">看不清?</a></font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
  
<input type="button" value=" 发表留言 " onClick="pubMsg()"/></td></tr>

  
</table>
  
</form>
  
  
</body>
  
</html>


2、ok.jsp 留言发布成功页面(示意)

<%@ page language="java" import="java.util.*" pageEncoding="gbk"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  
<body> 
    您的留言已提交!
<input type="button" value=" OK " onClick="window.history.go(-1)"/><br>
  
</body>
</html>

3、org.javachina.util.VerifyCode.java 验证码生成器类
package org.javachina.util;

import java.util.Random;
import java.awt.image.BufferedImage;
import java.awt.Graphics;
import java.awt.Font;
import java.awt.Color;


/**
 * 验证码生成器类,可生成数字、大写、小写字母及三者混合类型的验证码。
 * 支持自定义验证码字符数量;
 * 支持自定义验证码图片的大小;
 * 支持自定义需排除的特殊字符;
 * 支持自定义干扰线的数量;
 * 支持自定义验证码图文颜色
 * 
@author org.javachina
 * 
@version 1.01
 
*/
public class VerifyCode {
    
    
/**
     * 验证码类型为仅数字 0~9
     
*/
    
public static final int TYPE_NUM_ONLY=0;

    
/**
     * 验证码类型为仅字母,即大写、小写字母混合
     
*/
    
public static final int TYPE_LETTER_ONLY=1;
    
    
/**
     * 验证码类型为数字、大写字母、小写字母混合
     
*/
    
public static final int TYPE_ALL_MIXED=2;

    
/**
     * 验证码类型为数字、大写字母混合
     
*/
    
public static final int TYPE_NUM_UPPER=3;        
    
    
/**
     * 验证码类型为数字、小写字母混合
     
*/
    
public static final int TYPE_NUM_LOWER=4;    
    
    
/**
     * 验证码类型为仅大写字母
     
*/
    
public static final int TYPE_UPPER_ONLY=5;
    
    
/**
     * 验证码类型为仅小写字母
     
*/
    
public static final int TYPE_LOWER_ONLY=6;
    
        
    
private VerifyCode(){}

    
/**
     * 生成验证码字符串
     * 
@param type 验证码类型,参见本类的静态属性
     * 
@param length 验证码长度,大于0的整数
     * 
@param exChars 需排除的特殊字符(仅对数字、字母混合型验证码有效,无需排除则为null)
     * 
@return 验证码字符串
     
*/
    
public static String generateTextCode(int type,int length,String exChars){
        
        
if(length<=0return "";
        
        StringBuffer code
=new StringBuffer();
        
int i=0;
        Random r
=new Random();
        
        
switch(type)
        {
        
        
//仅数字
        case TYPE_NUM_ONLY:
            
while(i<length){
                
int t=r.nextInt(10);
                
if(exChars==null||exChars.indexOf(t+"")<0){//排除特殊字符
                    code.append(t);
                    i
++;
                }
            }
            
break;
            
        
//仅字母(即大写字母、小写字母混合)
        case TYPE_LETTER_ONLY:
            
while(i<length){
                
int t=r.nextInt(123);
                
if((t>=97||(t>=65&&t<=90))&&(exChars==null||exChars.indexOf((char)t)<0)){
                    code.append((
char)t);
                    i
++;    
                }
            }
            
break;
        
        
//数字、大写字母、小写字母混合
        case TYPE_ALL_MIXED:
            
while(i<length){
                
int t=r.nextInt(123);
                
if((t>=97||(t>=65&&t<=90)||(t>=48&&t<=57))&&(exChars==null||exChars.indexOf((char)t)<0)){
                    code.append((
char)t);
                    i
++;
                }
            }
            
break;    
            
        
//数字、大写字母混合
        case TYPE_NUM_UPPER:
            
while(i<length){
                
int t=r.nextInt(91);
                
if((t>=65||(t>=48&&t<=57))&&(exChars==null||exChars.indexOf((char)t)<0)){
                    code.append((
char)t);
                    i
++;
                }
            }
            
break;        
            
        
//数字、小写字母混合
        case TYPE_NUM_LOWER:
            
while(i<length){
                
int t=r.nextInt(123);
                
if((t>=97||(t>=48&&t<=57))&&(exChars==null||exChars.indexOf((char)t)<0)){
                    code.append((
char)t);
                    i
++;
                }
            }
            
break;        
            
        
//仅大写字母
        case TYPE_UPPER_ONLY:
            
while(i<length){
                
int t=r.nextInt(91);
                
if((t>=65)&&(exChars==null||exChars.indexOf((char)t)<0)){
                    code.append((
char)t);
                    i
++;
                }
            }
            
break;                
            
        
//仅小写字母
        case TYPE_LOWER_ONLY:
            
while(i<length){
                
int t=r.nextInt(123);
                
if((t>=97)&&(exChars==null||exChars.indexOf((char)t)<0)){
                    code.append((
char)t);
                    i
++;
                }
            }
            
break;                
        
        }
        
        
return code.toString();
    }

    
    
/**
     * 已有验证码,生成验证码图片
     * 
@param textCode 文本验证码
     * 
@param width 图片宽度
     * 
@param height 图片高度
     * 
@param interLine 图片中干扰线的条数
     * 
@param randomLocation 每个字符的高低位置是否随机
     * 
@param backColor 图片颜色,若为null,则采用随机颜色
     * 
@param foreColor 字体颜色,若为null,则采用随机颜色
     * 
@param lineColor 干扰线颜色,若为null,则采用随机颜色
     * 
@return 图片缓存对象
     
*/
    
public static BufferedImage generateImageCode(String textCode,int width,int height,int interLine,boolean randomLocation,Color backColor,Color foreColor,Color lineColor){
        
        
        BufferedImage bim
=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
        Graphics g
=bim.getGraphics();
        
        
//画背景图
        g.setColor(backColor==null?getRandomColor():backColor);
        g.fillRect(
0,0,width,height);
        
        
//画干扰线
        Random r=new Random();
        
if(interLine>0){
    
            
int x=0,y=0,x1=width,y1=0;
            
for(int i=0;i<interLine;i++){
                g.setColor(lineColor
==null?getRandomColor():lineColor);
                y
=r.nextInt(height);
                y1
=r.nextInt(height);
    
                g.drawLine(x,y,x1,y1);
            }
        }
        
        
//写验证码
        
        
//字体大小为图片高度的80%
        int fsize=(int)(height*0.8);
       
int fx=0;
        
int fy=fsize;

        g.setFont(
new Font(Font.DIALOG,Font.PLAIN,fsize));
        
        
//写验证码字符
        for(int i=0;i<textCode.length();i++){
            fy
=randomLocation?(int)((Math.random()*0.3+0.6)*height):fy;//每个字符高低是否随机
            g.setColor(foreColor==null?getRandomColor():foreColor);
            g.drawString(textCode.charAt(i)
+"",fx,fy);
            fx+=(width / textCode.length()) * (Math.random() * 0.3 + 0.8); //星风兄的高见,依据宽度浮动
        }

        
        g.dispose();
        
        
return bim;
    }

    
    
/**
     * 产生随机颜色
     * 
@return
     
*/
    
private static Color getRandomColor(){
        Random r
=new Random();
        Color c
=new Color(r.nextInt(256),r.nextInt(256),r.nextInt(256));
        
return c;
    }
  


}

4、org.javachina.servlet.VerifyCodeServlet.java 接受客户端请求,向客户端输出验证码
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            
throws ServletException, IOException {

        
        
//设置浏览器不缓存本页
        response.setHeader("Cache-Control""no-cache");
        
        
//生成验证码,写入用户session
        String verifyCode=VerifyCode.generateTextCode(VerifyCode.TYPE_UPPER_ONLY,4,null);
        request.getSession().setAttribute(
"verifyCode",verifyCode);
        
        
//输出验证码给客户端
        response.setContentType("image/jpeg");
        BufferedImage bim
=VerifyCode.generateImageCode(verifyCode, 90303,true,Color.WHITE,Color.BLACK,null);
        ImageIO.write(bim, 
"JPEG",response.getOutputStream());    
        
    }

5、org.javachina.servlet.PubMsgServlet.java 保存留言信息(比对验证码)
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            
throws ServletException, IOException {
        
        
//得到留言标题、内容及用户输入的验证码
        String msgTitle=request.getParameter("msgTitle");
        String msgContent
=request.getParameter("msgContent");
        String verifyCode
=request.getParameter("verifyCode");
        
        
//取session中的正确验证码
        String legalCode=null;
        
if(request.getSession().getAttribute("verifyCode")!=null)
            legalCode
=(String)(request.getSession().getAttribute("verifyCode"));
    
        String next;
        
        
//比较session中的验证码与用户输入是否一致(这里忽略了大小写)
        if(verifyCode!=null&&verifyCode.equalsIgnoreCase(legalCode)){
            
            
/*
             * 保存留言内容(省略)
             
*/
            
            
//指定下一个URL
            next=request.getContextPath()+"/ok.jsp";
            
        }
else
            next
="this";
        
        response.setContentType(
"text/plain");
        PrintWriter out 
= response.getWriter();
        out.print(next);
        out.flush();
        out.close();
        
    }


以上源码、文档下载地址: http://download.csdn.net/source/1851509

以上,望抛砖引玉,欢迎交流:hufoking@hotmail.com!



posted on 2009-11-27 14:03 IknowIT 阅读(4039) 评论(13)  编辑  收藏

评论

# re: Java通用验证码程序及应用示例(提供源码下载) 2009-11-27 17:09 零全零美

多谢博主写出这么好的总结,如果能加上汉字验证码,会更完美!  回复  更多评论   

# re: Java通用验证码程序及应用示例(提供源码下载) 2009-11-27 17:14 鹏飞liu

我最近也要用到验证码,但是不知道是直接调用jsp文件的形式还是servlet方式好一些,请指教!谢谢  回复  更多评论   

# re: Java通用验证码程序及应用示例(提供源码下载)[未登录] 2009-11-27 17:15 IKnowIT

多谢零全零美的建议!有时间把汉字验证码功能以及背景图片随机打点功能加上。  回复  更多评论   

# re: Java通用验证码程序及应用示例(提供源码下载)[未登录] 2009-11-27 17:17 IKnowIT

@鹏飞liu
也可以在jsp里直接写代码(或调用其他类)生成验证码,我习惯把这类的功能封装成工具类,便于在各种情况下重用。  回复  更多评论   

# re: Java通用验证码程序及应用示例(提供源码下载) 2009-11-28 23:10 bzp

不用session来传递验证码,行不行呢?当访问量比较大的时候,最好是尽量减少session的数量比较好  回复  更多评论   

# re: Java通用验证码程序及应用示例(提供源码下载) 2009-11-28 23:12 bzp

不用session来传递验证码,如何做?  回复  更多评论   

# re: Java通用验证码程序及应用示例(提供源码下载) 2009-11-29 11:49 零全零美

不用session那也只能用cookie了,页面展示的时候把验证码写入cookie,并设置适当的有效期,提交的时候再验证cookie里面的相应值!
不过我感觉,如果不是访问量大到离谱,用session存一下验证码也并没有什么不妥!  回复  更多评论   

# re: Java通用验证码程序及应用示例(提供源码下载) 2009-11-29 22:39 星风

一些小瑕疵:
1.生成图片时,传入的宽度没用上导致宽度过小时字符超出图片宽度。
// 写验证码字符
...
// fx += fsize * 0.9; // 原代码
// 根据传入宽度调整字距并随机±20%浮动
fx += (width / textCode.length()) * (Math.random() * 0.4 + 0.8);
...

2.VerifyCode.generateImageCode()的第二个重载无法拿到随机生成的字符串,没有任何意义。可以增加一个方法getLastTextCode()获取但又必须保证线程安全,还不如删掉。

3.随机颜色里
Color c = new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255));
应该是256

4.生成验证码字符串的那一大段swich完全可以简化,这样太繁冗了。
可以这样写(只是一个思路,没有测试):
// 添加一个静态字段
static char[] chars = {数字,大写字母,小写字母};
...
// 方法内替换switch
int begin = 0;
int end = 0;
switch (type) {
case TYPE_NUM_ONLY:
begin = 0;
end = 10; // 第10个不是数字
break;
// 其他的case...
}
while (i < length) {
int t = r.nextInt(end - begin); // 如果end设为9,这里要+1
code.append(chars[t + begin]);
i++;
}
...


最后感谢楼主的分享。  回复  更多评论   

# re: Java通用验证码程序及应用示例(提供源码下载)[未登录] 2009-11-30 10:29 IKnowIT

@星风
多谢您的意见,受教了。

第1条:非常高明,我原来没有考虑到,已经修改。多谢!
具体浮动的数,上浮20%有时字符会超出边界,测试一下可能:fx += (width / textCode.length()) * (Math.random() * 0.3 + 0.8); //0.4改为0.3更合适。

第2条:原是想对外提供一个直接生成图片的方法,但正如兄所说,如果用户没有textCode确实也没用。已经去掉。

第3条:应该是256,值包括255.

第4条:有一定道理,只是当验证码是两两混合或者三者混合时,需要考虑到验证码的每个数都是从其中随机抽取的,实际写起来代码量可能也不少。

最后,非常感谢如星风兄此好的建议,受益匪浅。  回复  更多评论   

# re: Java通用验证码程序及应用示例(提供源码下载) 2010-09-26 10:03 来访者

谢谢  回复  更多评论   

# re: Java通用验证码程序及应用示例(提供源码下载) 2012-04-16 16:36 宋军明

多发点  回复  更多评论   

# re: Java通用验证码程序及应用示例(提供源码下载) 2014-05-05 21:14 zuidaima

可以参考代码:java随机动态生成汉字验证码图片的实例代码分享,下载地址:http://www.zuidaima.com/share/1809721113234432.htm  回复  更多评论   

# re: Java通用验证码程序及应用示例(提供源码下载) 2014-07-14 15:03 刘鹏

楼主,为什么我把代码都复制了,验证码不显示呢?显示的是个x  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航:
 
<2014年7月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

导航

统计

常用链接

留言簿

随笔档案

搜索

最新评论