zoukankan      html  css  js  c++  java
  • [Java] 识别图片验证码

    现在大多数网站都采用了验证码来防止暴力破解或恶意提交。但验证码真的就很安全吗?真的就不能被机器识别??
    我先讲讲我是怎么实现站外提交留言到一个网站的程序。
    这个网站的留言版大致如下:




    我一看这种简单的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 = {
            
    512104545562436190 },    // '0'
            148931080136348222 },    // '1'
            51197139469273663 },     // '2'
            51197140617045598 },     // '3'
            35168914586948743 },     // '4'
            106548639817045598 },    // '5'
            239208494830871646 },    // '6'
            106562368469239824 },    // '7'
            512104542562436190 },    // '8'
            512104547486805660 }
        }
    ;                               // '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(74);

            
    // System.out.println(intBgColor);

            
    // 初始化图象原点座标
            pOrigin = new Point(00);
        }


        

        
    private void getBaseInfo() {
            System.out.println(intBgColor 
    + "|" + intCharColor);
            System.out.println(intMinX 
    + "|" + intMinY + "|" + intMaxX + "|" + intMaxY);
        }


        

        
    private Point[] getCharRange(int intNo) {

            
    // 左上右下点座标
            Point pTopLeft     = new Point(00);
            Point pBottomRight 
    = new Point(00);

            
    // 左上点
            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((
    float1true);
            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...
  • 相关阅读:
    关于回调地狱
    node.js 关于跨域和传递给前台参数
    关于js的当前日期的格式化,和两个日期之间的相减的天数
    CSS 关于让页面的高度达到电脑屏幕的底部
    前端 为什么我选择用框架而不是Jquery
    关于webpack打包图片的路径问题
    关于webpack打包js和css
    微信小程序网络请求的setDate
    WinSocket聊天程序实例(多线程)
    Orcal的JDBC数据连接方式
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/3531874.html
Copyright © 2011-2022 走看看