Java、Servlet生成验证码由浅到深

不BB,先上原理性的代码(基本能力强的看代码就理解了):

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;

public class Test {
    public static void main(String[] args) {
        final int WIDTH = 100;      //验证码的宽度
        final int HEIGHT = 30;      //验证码的高度
        final int SIZE = 25;        //字体的大小
        final int LENGTH = 4;       //验证码的个数

        //所有可能当验证码的字符(去掉了数字1和字母小l,防止辨识错误)
        char[] chs = "023456789abcdefghijkmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
        Random random = new Random();       //随机类,用以生成字符串数组的下标,以此来选中验证码字符
        //新建一张图片
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics grap = image.getGraphics();    //获取图形,以便进行编辑

        grap.setColor(Color.LIGHT_GRAY);        //拿起哪种颜料
        grap.fillRect(0, 0, WIDTH, HEIGHT);     //使用拿起的颜料给图形背景上色

        for (int i=0; i<LENGTH; i++) {      //生成随机验证码并画到图形上
            int pos = random.nextInt(chs.length);
            Font font = new Font("宋体", Font.BOLD, SIZE);        //字体对象
            grap.setFont(font);     //设置字体属性
            grap.setColor(Color.MAGENTA);  //设置字体的颜色
            grap.drawString(chs[pos] + "", (i*SIZE)+4, 24);     //在(x, y)处画出指定字符
        }
        try {
            //以图片的形式在E:\checkcode.jpg处存放验证码
            ImageIO.write(image, "JPG", new File("E:/checkcode.jpg"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

上面是我参考多篇博客简化的原理性代码,方便理解能力强的直接get要点,
然后以下就是我自己进阶的过程(其中关于Servlet的在最后面并显示到网页上的在最后面),不太建议大家看,因为排版不太友好。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
这些天
在写一个小项目准备找实习
但是却在生成验证码这块卡住了
在网上看了许多的博客
基本都是清一色的一大串代码然后加几行注释
看得有些云里雾里不知从何下手
然后就写了这篇博客
边写边学习,在自己会的同时也希望带动更多的需要者更容易的入手
这也算是贯彻落实了我国的方针政策——“先懂带动后懂”

零、实现验证码的主要思想

1. 将可以当验证码的字符存到一个字符串数组里面,随机生成下标来选择验证码字符
2. 使用java.util.Random类的nextInt(int bound)来生成[0, bound)区间内的整数,当下标
3. 使用java.awt.image.BufferedImage来生成一张图片
4. 使用java.awt.Graphics来编辑图片,即在上面画验证码
5. 最后一步是用javax.imageio.ImageIO的write()方法将验证码图片存储到或者发送到指定的地方

一、简化版验证码的生成

具体思路已经在上面讲了,所谓简化版,就是指去掉大部的修饰和应用背景然后单纯地实现一个简单可见的图片
首先,先上个效果图,激发一下大家的斗志,哈哈哈
这里写图片描述
一些类和方法的介绍如下(基础好的可以直接看源码,源码有解释,容易理解):

  • BufferedImage类是一个存储图片的类,可以简单的理解它的一个对象就是一张图片。它的其中一个构造函数有三个参数,第一个是图片的宽度,第二个是图片的高度,第三个,虽然有点羞涩,但是我还是大方地承认了,我也不懂,我只是单纯地使用了和别人一样的参数——BufferedImage.TYPE_INT_RGB。这个类有一个无参数方法getGraphics(),是用来返回一个可编辑的图形Graphics对象的。
  • Graphics类,简单的理解为图形类,在图形上面,我们可以画上我们的验证码。我们使用它的drawString()方法来将我们随机生成的验证码字符画到上面,这个方法有三个参数,第一个是一个String类型,即我们需要画到这个图形上的字符,第二、第三个参数为坐标(x,y),用来指定从哪里开始画给定的String。除了这个方法还有许多我们可能用到的方法如设置字体、颜色等,这里就不一一介绍了,读者应该看一眼源码就能明白。
  • Random类是用来生成随机数的,里面有一个方法nextInt(int bound),用来返回一个在区间[0, bound)内的整数。我们使用这个类来生成一个整数,这个整数是一个下标,用来定位验证码字符。
  • ImageIO类,我很无耻地承认了吧,我也不懂用来干嘛的,我就用过它的一个static方法write(),这个方法有其中一个形态有三个参数,第一个是BufferedImage的对象,第二个是图片的格式如jpg\png等,String类型的,第三个参数是一个位置吧,用来显示第一个参数的,这里使用的是new File(“E:/checkcode.jpg”),很明显,是将BufferedImage的对象以图片的形式放在操作系统的E盘下。

以下是具体的代码,结合注解和前面的介绍,相信不难理解

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;

public class Test {
    public static void main(String[] args) {
        final int WIDTH = 100;      //验证码的宽度
        final int HEIGHT = 30;      //验证码的高度
        final int SIZE = 25;        //字体的大小
        final int LENGTH = 4;       //验证码的个数

        //所有可能当验证码的字符(去掉了数字1和字母小l,防止辨识错误)
        char[] chs = "023456789abcdefghijkmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
        Random random = new Random();       //随机类,用以生成字符串数组的下标,以此来选中验证码字符
        //新建一张图片
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics grap = image.getGraphics();    //获取图形,以便进行编辑

        grap.setColor(Color.LIGHT_GRAY);        //拿起哪种颜料
        grap.fillRect(0, 0, WIDTH, HEIGHT);     //使用拿起的颜料给图形背景上色

        for (int i=0; i<LENGTH; i++) {      //生成随机验证码并画到图形上
            int pos = random.nextInt(chs.length);
            Font font = new Font("宋体", Font.BOLD, SIZE);        //字体对象
            grap.setFont(font);     //设置字体属性
            grap.setColor(Color.MAGENTA);  //设置字体的颜色
            grap.drawString(chs[pos] + "", (i*SIZE)+4, 24);     //在(x, y)处画出指定字符
        }
        try {
            //以图片的形式在E:\checkcode.jpg处存放验证码
            ImageIO.write(image, "JPG", new File("E:/checkcode.jpg"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

二、进阶版验证码的生成

如果你看懂了上面的代码,并且实现了出来,那么恭喜你
你学到的这些并没有什么卵用
(⊙o⊙)…当然是开玩笑了,别紧张别紧张,哈哈哈
上面那个简化版是一个基础,有了这个基础,再在这个基础之上进一步修饰
我们就能画出我们很中意的验证码图片了
想想是不是很激动,我也很激动,因为这一部分我也不会,正在学习…
咳咳,无敌的我又回来了
关于这个进阶,就加了一些干扰线还有旋转
效果如下
这里写图片描述
具体用到的方法和类介绍如下

  • 字符的旋转使用的是Graphics2D类的rotate()方法,这个方法有三个参数,第一个是虽然不太懂,但是估摸着是和旋转的角度弧度有关,实践证明我是对的,第二个参数和第三个参数给出旋转字符的坐标。代码里面主要写法如下((Graphics2D)grap).rotate(angle*Math.PI/180, x, y);。
  • 干扰线的显示使用的是Graphics类的drawLine()方法,这个方法有四个参数,顺序给出两个二维坐标,根据这两个坐标,即可画出一条干扰线。

以下是上次简化版的代码升级之后的代码

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;

public class Test {
    public static void main(String[] args) {
        String checkCode = drawCheckCode();
        System.out.println(checkCode);
    }

    public static String drawCheckCode() {
        /*如果要改变验证码的属性
         *可以更改WIDTH、HEIGHT、SIZE、LENGTH、LINENUM
         * (x, y)坐标
         * 旋转角度angle
         * 干扰线的值
         * 以上这些基本就可以
         */
        final int WIDTH = 100;      //验证码的宽度
        final int HEIGHT = 30;      //验证码的高度
        final int SIZE = 25;        //字体的大小
        final int LENGTH = 4;       //验证码的个数
        final int LINENUM = 5;      //干扰线的条数

        char[] str = new char[LENGTH];
        //所有可能当验证码的字符(去掉了数字1和字母小l,还有数字0和字母大O和字母小o,防止辨识错误)
        char[] chs = "23456789abcdefghijkmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ".toCharArray();
        Random random = new Random();       //随机类,用以生成字符串数组的下标,以此来选中验证码字符
        //新建一张图片
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics grap = image.getGraphics();    //获取图形,以便进行编辑

        grap.setColor(Color.LIGHT_GRAY);        //拿起哪种颜料
        grap.fillRect(0, 0, WIDTH, HEIGHT);     //使用拿起的颜料给图形背景上色

        for (int i=0; i<LENGTH; i++) {      //生成随机验证码并画到图形上
            int pos = random.nextInt(chs.length);
            int angle = random.nextInt() % 40;
            int x = (i*SIZE)+5;
            int y = 24;

            str[i] = chs[pos];      //记录验证码

            Font font = new Font("宋体", Font.BOLD, SIZE);        //字体对象
            grap.setFont(font);     //设置字体属性
            ((Graphics2D)grap).rotate(angle*Math.PI/180, x, y);   //旋转
            grap.setColor(Color.MAGENTA);  //设置字体的颜色
            grap.drawString(chs[pos] + "", x, y);     //在(x, y)处画出指定字符
            ((Graphics2D)grap).rotate(-angle*Math.PI/180, x, y);  //转回来
        }

        //画干扰线
        for (int i=0; i<LINENUM; i++) {
            int rgb = random.nextInt(256);
            int x1 = random.nextInt(WIDTH);
            int y1 = random.nextInt(HEIGHT);
            int x2 = random.nextInt(WIDTH);
            int y2 = random.nextInt(HEIGHT);
            grap.setColor(new Color(rgb));
            grap.drawLine(x1, y1, x2, y2);      //(x1, y1)到(x2, y2)的线段
        }

        String imageFormat = "jpg";
        try {
            ImageIO.write(image, imageFormat, new File("E:/checkcode." + imageFormat));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new String(str);
    }
}

三、实用版验证码的生成(Servlet和HTML)

相信能坚持看到这里的人,都是会部署Servlet的人,
所以关于Servlet的基础配置我就不讲了,主要讲在前面代码的基础之上所添加或者修改的东西,
首先,按照国际惯例,我们先讲一下大体的思路

  1. 我们需要两个代码文件,一个是生成验证码图片并且发送给客户端的Servlet类,一个是接收验证码图片的HTML。
  2. 生成验证码图片的Servlet类,需要将生成的图片以流的形式传出去,谁访问这个类的地址,谁就能获取到验证码图片。一般是使用形如ImageIO.write(image, "jpg", res.getOutputStream());的代码进行图片流的传送。
  3. 比如这个Servlet类你给定的URL地址为/login/create,你只需要在客户端设置一个<img id='checkimg' src='/login/create' alt='无法显示' />这个标签,src属性指向Servlet类的URL地址,即可接收验证码图片。这就是那个HTML文件的功能。
  4. 然后我认为其中一个技术难点在于怎么在点击验证码图片的时候,刷新验证码。这个我主要靠更改<img>标签的属性src的值达到的,因为更改前和更改后的src如果一样,浏览器是不会重新发送请求的,所以我每次使用的src属性最后面都会加一个new Date(),因为参数不一样了,所以就能请求了。如 src = ‘/login/create?’ + new Date()。

具体代码如下(生成验证码的代码主要改了ImageIO.write()那里的第三个参数):

CreateCheckCode.java
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

public class CreateCheckCode extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse res) {
        res.setContentType("image/jpg");
        HttpSession session = req.getSession();
        String checkCode = drawCheckCode(res);
        session.setAttribute("checkCode", checkCode);
    }

    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse res) {
        doGet(req, res);
    }

    //生成验证码
    public String drawCheckCode(HttpServletResponse res) {
        /*如果要改变验证码的属性
         *可以更改WIDTH、HEIGHT、SIZE、LENGTH、LINENUM
         * (x, y)坐标
         * 旋转角度angle
         * 干扰线的值
         * 以上这些基本就可以
         */
        final int WIDTH = 100;      //验证码的宽度
        final int HEIGHT = 30;      //验证码的高度
        final int SIZE = 25;        //字体的大小
        final int LENGTH = 4;       //验证码的个数
        final int LINENUM = 5;      //干扰线的条数

        char[] str = new char[LENGTH];
        //所有可能当验证码的字符(去掉了数字1和字母小l,还有数字0和字母大O和字母小o,防止辨识错误)
        char[] chs = "23456789abcdefghijkmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ".toCharArray();
        Random random = new Random();       //随机类,用以生成字符串数组的下标,以此来选中验证码字符
        //新建一张图片
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics grap = image.getGraphics();    //获取图形,以便进行编辑

        grap.setColor(Color.LIGHT_GRAY);        //拿起哪种颜料
        grap.fillRect(0, 0, WIDTH, HEIGHT);     //使用拿起的颜料给图形背景上色

        for (int i=0; i<LENGTH; i++) {      //生成随机验证码并画到图形上
            int pos = random.nextInt(chs.length);
            int angle = random.nextInt() % 40;
            int x = (i*SIZE)+5;
            int y = 24;

            str[i] = chs[pos];      //记录验证码

            Font font = new Font("宋体", Font.BOLD, SIZE);        //字体对象
            grap.setFont(font);     //设置字体属性
            ((Graphics2D)grap).rotate(angle*Math.PI/180, x, y);   //旋转
            grap.setColor(Color.MAGENTA);  //设置字体的颜色
            grap.drawString(chs[pos] + "", x, y);     //在(x, y)处画出指定字符
            ((Graphics2D)grap).rotate(-angle*Math.PI/180, x, y);  //转回来
        }

        //画干扰线
        for (int i=0; i<LINENUM; i++) {
            int rgb = random.nextInt(256);
            int x1 = random.nextInt(WIDTH);
            int y1 = random.nextInt(HEIGHT);
            int x2 = random.nextInt(WIDTH);
            int y2 = random.nextInt(HEIGHT);
            grap.setColor(new Color(rgb));
            grap.drawLine(x1, y1, x2, y2);      //(x1, y1)到(x2, y2)的线段
        }

        String imageFormat = "jpg";
        try {
            ImageIO.write(image, imageFormat, res.getOutputStream());
        } catch (IOException e) {
            System.out.println("IOException...");
            //e.printStackTrace();
        }
        return new String(str);
    }
}

HTML代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Test</title>
    <script type="text/javascript">
        function changeCheckimg(context) {
            //最后那个new Date()可以看做参数,因为只有src发生了改变,浏览器才会重新请求
            context.src = "/login/create?" + new Date();
        }
    </script>
</head>
<body>
<img id='checkimg' src='/login/create' alt='无法显示' onclick="javascript:changeCheckimg(this);" />
</body>
</html>

至此,关于生成验证码的知识全部搞定,加上写这个博客,用了两天,
自己真笨,居然用了这么长的时间,再贴一下效果图,让自己高兴一下,感觉自己傻逼傻逼棒棒的
这里写图片描述
关于这个Servlet生成验证码图片并且发送,我再啰嗦一两点

  • 主要使用BufferedImage类和Graphics类,ImageIO.write()静态方法和Random类
  • 在Servlet使用流来发送图片,这就使用到了response.getOutputStream()
  • 在浏览器显示图片的时候,使用改变<img>标签的src属性达到点击刷新的功能
    呼…
    舒服…
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值