Java通用验证码程序及应用示例

5 篇文章 0 订阅
5 篇文章 0 订阅

把之前项目中写的一个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提交功能


<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> <% @ 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 留言发布成功页面(示意)

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> <% @ 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 验证码生成器类

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> 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 <= 0 return   "" ;
        
        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 接受客户端请求,向客户端输出验证码

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->      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,  90 30 3 , true ,Color.WHITE,Color.BLACK, null );
        ImageIO.write(bim, 
" JPEG " ,response.getOutputStream());    
        
    }


5、org.javachina.servlet.PubMsgServlet.java 保存留言信息(比对验证码)

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->      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();
        
    }

一些小瑕疵:
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++;
}
...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值