博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[Java] 识别图片验证码
阅读量:6755 次
发布时间:2019-06-26

本文共 9057 字,大约阅读时间需要 30 分钟。

现在大多数网站都采用了验证码来防止暴力破解或恶意提交。但验证码真的就很安全吗?真的就不能被机器识别??

我先讲讲我是怎么实现站外提交留言到一个网站的程序。
这个网站的留言版大致如下:
我一看这种简单的4位数字验证码,马上就感觉到有戏了。直觉告诉我让电脑来识别这些图片验证码据对简单o(∩_∩)o...
首先我马上在这个页面用右键菜单看源代码
知道验证码获取页面后 你可以直接用 http://www.XXXX.com/imgchk/validatecode.asp 这样去访问你会发现你打开的就是一个验证码图片。
对的其实返回的就是图片文件的2进制流而已。接着先用右键保存一张验证码的图片。因为要开始分析这张图片了,什么用什么工具?PhotoShop????不用就一般的画图工具就可以了。我们要搞清楚的是这几个数字分别占几个像素就可以了。
可以看出 一个数字5*9 

也就是45个像素。恩 这就可以了 另外我们可以看出默认区域就是白色
(姑且说是白色因为我们肉眼看就是白色)
那么我的程序识别原理就是固定去扫描这45个像素点。看每个点的颜色是不是和默认的颜色一致
一致的话就标记为0 ,不一致就标记为1 。
如一个数子是2 那么我的程序扫描出来的图像就应该是:
011110
100001
000001
000001
000010
000100
001000
010000
100000
111111
如果一个数字是7那么扫描出来的图像就是:
111111
100001
000010
000010
000100
000100
001000
001000
010000
010000
恩,就这么简单呵呵。下面给出图像 扫描的java类 (不好意思,在我会的语言里面除开java就剩sql了)
package
 com.util;
//
~--- JDK imports ------------------------------------------------------------
import
 com.sun.image.codec.jpeg.JPEGCodec;
import
 com.sun.image.codec.jpeg.JPEGEncodeParam;
import
 com.sun.image.codec.jpeg.JPEGImageEncoder;
import
 java.awt.
*
;
import
 java.awt.image.
*
;
import
 java.io.
*
;
import
 java.io.FileOutputStream;
import
 java.io.OutputStream;
import
 java.net.
*
;
import
 javax.imageio.
*
;
import
 javax.imageio.ImageIO;
public
 
class
 ImgIdent 
{
    
//
 数字字符比特表
    
private
 
final
 
long
[][] NUMERIC 
=
 
{
        
512104545
562436190
 }
,    
//
 '0'
        
148931080
136348222
 }
,    
//
 '1'
        
511971394
69273663
 }
,     
//
 '2'
        
511971406
17045598
 }
,     
//
 '3'
        
35168914
586948743
 }
,     
//
 '4'
        
1065486398
17045598
 }
,    
//
 '5'
        
239208494
830871646
 }
,    
//
 '6'
        
1065623684
69239824
 }
,    
//
 '7'
        
512104542
562436190
 }
,    
//
 '8'
        
512104547
486805660
 }
    }
;                               
//
 '9'
    
//
 字框高
    
private
 
int
 intCharHeight 
=
 
10
;
    
//
 字框横向间隙
    
private
 
int
 intCharSpaceH 
=
 
5
;
    
//
 字框纵向间隙
    
private
 
int
 intCharSpaceY 
=
 
1
;
    
//
 字框宽
    
private
 
int
           intCharWidth 
=
 
5
;
    
private
 
int
           IntImgHeight;
    
private
 BufferedImage img;
    
private
 
int
           intBgColor;
    
private
 
int
           intCharColor;
    
private
 
int
           intImgWith;
    
private
 
int
           intMaxX;
    
private
 
int
           intMaxY;
    
private
 
int
           intMinX;
    
private
 
int
           intMinY;
    
//
 座标原点
    
private
 Point  pOrigin;
    
private
 String strNum;
    
    
public
 ImgIdent(BufferedImage img) 
throws
 IOException 
{
        
this
.img 
=
 img;
        init();
    }
    
    
public
 ImgIdent(File file) 
throws
 IOException 
{
        img 
=
 ImageIO.read(file);
        init();
    }
    
    
public
 ImgIdent(URL url) 
throws
 IOException 
{
        img 
=
 ImageIO.read(url);
        init();
    }
    
    
private
 
void
 init() 
{
        
//
 得到图象的长度和宽度
        intImgWith   
=
 img.getWidth();
        IntImgHeight 
=
 img.getHeight();
        
//
 得到图象的背景颜色
        intBgColor 
=
 img.getRGB(
7
4
);
        
//
 System.out.println(intBgColor);
        
//
 初始化图象原点座标
        pOrigin 
=
 
new
 Point(
0
0
);
    }
    
    
private
 
void
 getBaseInfo() 
{
        System.out.println(intBgColor 
+
 
"
|
"
 
+
 intCharColor);
        System.out.println(intMinX 
+
 
"
|
"
 
+
 intMinY 
+
 
"
|
"
 
+
 intMaxX 
+
 
"
|
"
 
+
 intMaxY);
    }
    
    
private
 Point[] getCharRange(
int
 intNo) 
{
        
//
 左上右下点座标
        Point pTopLeft     
=
 
new
 Point(
0
0
);
        Point pBottomRight 
=
 
new
 Point(
0
0
);
        
//
 左上点
        pTopLeft.x 
=
 pOrigin.x 
+
 intCharWidth 
*
 (intNo 
-
 
1
+
 intCharSpaceH 
*
 (intNo 
-
 
1
);
        pTopLeft.y 
=
 pOrigin.y;
        
//
 右下点
        pBottomRight.x 
=
 
1
 
+
 pOrigin.x 
+
 intCharWidth 
*
 intNo 
+
 intCharSpaceH 
*
 (intNo 
-
 
1
-
 
1
;
        pBottomRight.y 
=
 pOrigin.y 
+
 intCharHeight 
-
 
1
;
        
return
 
new
 Point[] 
{ pTopLeft, pBottomRight }
;
    }
    
    
private
 
char
 getBit(
int
 x, 
int
 y) 
{
        
int
 intCurtColor;
        intCurtColor 
=
 img.getRGB(x, y);
        
//
System.out.println("[" + x + "," + y + "]" + intCurtColor + "==" + intBgColor + "==>" + (Math.abs(intCurtColor) >7308252));
//
      return (Math.abs(intCurtColor) >= 5689325)
//
              ? '0'
//
              : '1';
        
return
 (intCurtColor 
==
 intBgColor)
               
?
 
'
0
'
               : 
'
1
'
;
        
//
 5689325    6008535
    }
    
    
private
 String getCharString(
int
 intNo) 
{
        
//
 本字符的左上右下点座标
        Point[] p            
=
 getCharRange(intNo);
        Point   pTopLeft     
=
 p[
0
];
        Point   pBottomRight 
=
 p[
1
];
        
//
 换算边界值
        
int
 intX1, intY1, intX2, intY2;
        intX1 
=
 pTopLeft.x;
        intY1 
=
 pTopLeft.y;
        intX2 
=
 pBottomRight.x;
        intY2 
=
 pBottomRight.y;
//
      System.out.println("intX1=" + intX1);
//
      System.out.println("intY1=" + intY1);
//
      System.out.println("intX2=" + intX2);
//
      System.out.println("intY2=" + intY2);
        
//
 在边界内循环取象素
        
int
    i, j;
        String strChar 
=
 
""
;
        
for
 (i 
=
 intY1; i 
<=
 intY2; i
++
{
            
for
 (j 
=
 intX1; j 
<=
 intX2; j
++
{
                System.out.print(getBit(j, i));
                strChar 
=
 strChar 
+
 getBit(j, i);
            }
            System.out.println();
        }
        System.out.println();
        
return
 strChar;
    }
    
    
public
 
int
 getNum(
int
 intNo) 
{
        
//
 取得位字符串
        String strChar 
=
 getCharString(intNo);
        
//
 System.out.println(intNo+"=="+strChar);
        
//
 取得串高位串和低位串
        String strCharHigh 
=
 strChar.substring(
0
, strChar.length() 
/
 
2
);
        String strCharLow  
=
 strChar.substring(strChar.length() 
/
 
2
);
        
//
 计算高位和低位值
        
long
 lCharHigh 
=
 Long.parseLong(strCharHigh, 
2
);
        System.out.println(lCharHigh);
        
long
 lCharLow 
=
 Long.parseLong(strCharLow, 
2
);
        System.out.println(lCharLow);
        
//
 在数字中循环比较
        
int
 intNum 
=
 
'
*
'
;
        
for
 (
int
 i 
=
 
0
; i 
<=
 
9
; i
++
{
            
if
 ((lCharHigh 
==
 NUMERIC[i][
0
]) 
&&
 (lCharLow 
==
 NUMERIC[i][
1
])) 
{
                intNum 
=
 i;
                
break
;
            }
 
else
 
{
                
if
 ((lCharHigh 
==
 
834533329
&&
 (lCharLow 
==
 
242870177
)) 
{
                    intNum 
=
 
6
;
                }
    
//
 834533329 242870177
                        
else
 
{
                    intNum 
=
 
1
;
                }
    
//
 默认为1   低位为    937393609  937393601
            }
        }
        
return
 intNum;
    }
    
    
public
 String getValidatecode(
int
 length) 
{
        String strNum 
=
 
""
;
        
for
 (
int
 i 
=
 
1
; i 
<=
 length; i
++
{
            
synchronized
 (
this
{
                strNum 
+=
 String.valueOf(getNum(i));
            }
        }
        
return
 strNum;
    }
    
    
public
 
void
 saveJPEG(BufferedImage iag, String savePath) 
throws
 FileNotFoundException, IOException 
{
        OutputStream     jos     
=
 
new
 FileOutputStream(savePath);
        JPEGImageEncoder encoder 
=
 JPEGCodec.createJPEGEncoder(jos);
        JPEGEncodeParam  jpegEP  
=
 JPEGCodec.getDefaultJPEGEncodeParam(iag);
        jpegEP.setQuality((
float
1
true
);
        encoder.encode(iag, jpegEP);
        jos.flush();
        jos.close();
    }
}
恩这样数字是可以识别出来了,可以我要怎么完成提交那块的工作呢?好在Apache已经为我做完了。我用了
HttpClient这样一个模拟Http协议的小工具包。我只要往那个 Add_MSG.asp里面提交就完了。
package
 com.util;
//
~--- non-JDK imports --------------------------------------------------------
import
 org.apache.commons.httpclient.
*
;
import
 org.apache.commons.httpclient.methods.GetMethod;
import
 org.apache.commons.httpclient.methods.PostMethod;
import
 org.apache.commons.httpclient.params.HttpClientParams;
import
 org.apache.commons.httpclient.params.HttpMethodParams;
//
~--- JDK imports ------------------------------------------------------------
import
 java.awt.image.BufferedImage;
import
 java.io.InputStream;
import
 javax.imageio.ImageIO;
public
 
class
 MyHttpClient 
{
    
    
public
 
synchronized
 
void
 doSomeThing(String title, String name, String Content, String proIP, 
int
 port,
            
boolean
 usePro) 
{
        
//
 构造HttpClient的实例
        HttpClient       httpClient   
=
 
new
 HttpClient();
        HttpClientParams clientParams 
=
 
new
 HttpClientParams();
        
//
 隐藏自己请求相关的信息
        clientParams.setParameter(
"
http.useragent
"
"
Mozilla/4.0 (compatible; FIREFOX 9.0; IBM AIX 5)
"
);
        
//
 httpClient.getHttpConnectionManager().getParams().setSoTimeout(30 * 1000);
        clientParams.setHttpElementCharset(
"
GBK
"
);
        HttpState httpState 
=
 
new
 HttpState();
        httpClient.setParams(clientParams);
        httpClient.getParams().setParameter(HttpClientParams.HTTP_CONTENT_CHARSET, 
"
GBK
"
);
        httpClient.setState(httpState);
        clientParams.setVersion(HttpVersion.HTTP_1_1);
        
//
 httpClient.getHostConfiguration().setProxy("148.233.159.58", 3128);
        
if
 (usePro)    
//
 使用代理
        
{
            httpClient.getHostConfiguration().setProxy(proIP, port);
        }
        
//
 创建GET方法的实例
        GetMethod getMethod 
=
 
new
 GetMethod(
"
http://www.XXXcom/Guestbook/imgchk/validatecode.asp
"
);
        
//
 使用系统提供的默认的恢复策略
        getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, 
new
 DefaultHttpMethodRetryHandler());
        
try
 
{
            
//
 执行getMethod
            
int
 statusCode 
=
 httpClient.executeMethod(getMethod);
            
//
 System.out.println(statusCode);
            
if
 (statusCode 
!=
 HttpStatus.SC_OK) 
{
                System.err.println(
"
Method failed: 
"
 
+
 getMethod.getStatusLine());
            }
    
//
 读取内容
            InputStream inStream 
=
 getMethod.getResponseBodyAsStream();
            
//
 处理内容
            
//
 System.out.println(new String(responseBody));
            BufferedImage iag      
=
 ImageIO.read(inStream);
            ImgIdent      imgIdent 
=
 
new
 ImgIdent(iag);
            
//
 imgIdent.saveJPEG(iag, "C:/ddd.jpg");
            String validate 
=
 imgIdent.getValidatecode(
4
);
            System.out.println(validate);
            PostMethod method  
=
 
new
 PostMethod(
"
http://www.XXX.com/Guestbook/add_msg.asp
"
);
            String     connect 
=
 Content;
            String     Title   
=
 title;
            method.setParameter(
"
subject
"
, Title);
            method.setParameter(
"
g_name
"
, name);
            method.setParameter(
"
companyname
"
""
);
            method.setParameter(
"
mail
"
""
);
            method.setParameter(
"
homepageurl
"
"
http://
"
);
            method.setParameter(
"
pic
"
"
p5.gif
"
);
            method.setParameter(
"
validatecode
"
, validate);
            method.setParameter(
"
content
"
, connect);
//
          if (todo) {
            
int
 code 
=
 httpClient.executeMethod(method);
            
//
 String Stringresponse = new String(method.getResponseBodyAsString().getBytes("8859_1"));
            
//
 打印返回的信息
            
//
 System.out.println(Stringresponse);
//
          }
            method.releaseConnection();
//
          System.out.println(iag.getHeight());
//
          System.out.println(iag.getWidth());
//
          
//
背景 颜色
//
          intBgColor = iag.getRGB(38, 0);
//
          System.out.println("intBgColor=" + intBgColor);
//
//
//
          intBgColor = iag.getRGB(0, 0);
//
          System.out.println("intBgColor=" + intBgColor);
        }
 
catch
 (Exception e) 
{
            
//
 发生网络异常
            e.printStackTrace();
        }
 
finally
 
{}
        
//
 释放连接   getMethod.releaseConnection();  }
        getMethod.releaseConnection();
    }
}
恩 就这样了,最后结合SAF整成这样了。什么?为什么不用SWT?想过了SWING才是王道o(∩_∩)o...

转载地址:http://khgho.baihongyu.com/

你可能感兴趣的文章
12.02个人博客
查看>>
Notification通知代码简洁使用
查看>>
UIView 动画
查看>>
ssh加密公私钥
查看>>
快速部署Python应用:Nginx+uWSGI配置详解
查看>>
mybatis-generator生成数据对象
查看>>
java Queue中 add/offer,element/peek,remove/poll区别
查看>>
一个继承了抽象类的普通类的执行顺序
查看>>
Map集合中key不存在时使用toString()方法、valueOf()方法和强制转换((String))之间的区别...
查看>>
ArcIMS 开发学习笔记(一)
查看>>
leetcode_1095. Find in Mountain Array_[Binary Search]
查看>>
关于搭建haddoop分布式系统的全部过程复习
查看>>
简单使用SOCKET,TCP,UDP模式之间的通信
查看>>
js历史返回
查看>>
JavaWeb之JavaMail
查看>>
430. Flatten a Multilevel Doubly Linked List - Medium
查看>>
ASP.NET MVC学前篇之Lambda表达式、依赖倒置
查看>>
空白不曾停止。。。
查看>>
python递归——汉诺塔
查看>>
洛谷——1060 开心的金明
查看>>