验证码-支持多种背景+Gif

前言

Java登录验证码,支持多种背景干扰;同时支持动态Gif模式。


话不多说,先来个效果展示:

静态验证码:

1-1          1-2
1-3          1-4

1-5          1-6

GIF动态验证码:

2-1          2-2

2-3          2-4

2-5          2-6

步骤总览

  1. 需要一个能随机生成字符验证码的工具类-Randoms
  2. 定义一个验证码的抽象对象-Captcha
  3. PNG或者GIF输出随机生成的验证码图片
Randoms.java

能随机生成字符串的类

package ranpic;

import java.util.Random;

/**
 *
 * <p>随机生成指定长度的内容</p>
 * @date 2019/01/08
 * @author ffewi
 * @version 1.0
 */
public class Randoms {

    private static final Random RANDOM = new Random();
    //定义验证码字符
    public static final char ALPHA[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'G', 'K', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
            , 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};

    /**
     * 产生两个数之间的随机数
     *
     * @param min 小数
     * @param max 比min大的数
     * @return int 随机数字
     */
    public static int num(int min, int max) {
        return min + RANDOM.nextInt(max - min);
    }

    /**
     * 产生0--num的随机数,不包括num
     *
     * @param num 数字
     * @return int 随机数字
     */
    public static int num(int num) {
        return RANDOM.nextInt(num);
    }

    public static char alpha() {
        return ALPHA[num(0, ALPHA.length)];
    }
}


Captcha.java

验证码抽象对象

package ranpic;


import java.awt.*;
import java.io.OutputStream;
import java.util.ArrayList;

import static ranpic.Randoms.alpha;
import static ranpic.Randoms.num;

/**
 * <p>验证码抽象类,统一验证码的属性</p>
 *
 * @author ffewi
 * @version 1.0
 * @date 2019/01/08
 */
public abstract class Captcha {


    /**
     * 默认字体
     */
    protected Font font = new Font("Verdana", Font.ITALIC | Font.BOLD, 28);
    /**
     * 默认验证码随机字符长度
     */
    protected int len = 6;
    /**
     * 默认验证码显示跨度
     */
    protected int width = 150;
    /**
     * 默认验证码显示高度
     */
    protected int height = 40;
    /**
     * 随机字符串
     */
    protected String chars = null;

    /**
     * 背景填充模式
     * 1、圆形
     * 2、空心距形
     * 3、凸出立体距形
     * 4、凹进去的立体距形
     * 5、空心笛卡尔心形
     * 6、完美心形
     * 其他、默认无
     */
    protected CaptchaBgFillMode mode = CaptchaBgFillMode.ROUND;


    protected void drawBgByMode(Graphics2D g2d, CaptchaBgFillMode mode) {
        if (CaptchaBgFillMode.ROUND.equals(mode)) {
            g2d.drawOval(num(width), num(height), 5 + num(10), 5 + num(10));
        } else if (CaptchaBgFillMode.HOLLOW_SHAPE.equals(mode)) {
            g2d.drawRect(num(width), num(height), 5 + num(10), 5 + num(10));
        } else if (CaptchaBgFillMode.PROTRUDING_STEREO_SHAPE.equals(mode)) {
            g2d.draw3DRect(num(width), num(height), 5 + num(10), 5 + num(10), true);
        } else if (CaptchaBgFillMode.CONCAVE_STEREOSCOPIC_SHAPE.equals(mode)) {
            g2d.draw3DRect(num(width), num(height), 5 + num(10), 5 + num(10), false);
        } else if (CaptchaBgFillMode.HOLLOW_DESCARTES_HEART_SHAPE.equals(mode)) {
            drawHeart(g2d, 5 + num(width), 5 + num(height), CaptchaBgFillMode.HOLLOW_DESCARTES_HEART_SHAPE);
        } else if (CaptchaBgFillMode.PERFECT_HEART_SHAPE.equals(mode)) {
            drawHeart(g2d, 5 + num(width), 5 + num(height), CaptchaBgFillMode.PERFECT_HEART_SHAPE);
        } else {
            //
        }
    }

    /**
     * 生成随机字符数组
     *
     * @return 字符数组
     */
    protected char[] alphas() {
        char[] cs = new char[len];
        for (int i = 0; i < len; i++) {
            cs[i] = alpha();
        }
        chars = new String(cs);
        return cs;
    }

    /**
     * 给定范围获得随机颜色
     *
     * @return Color 随机颜色
     */
    protected Color color(int fc, int bc) {
        if (fc > 255)
            fc = 255;
        if (bc > 255)
            bc = 255;
        int r = fc + num(bc - fc);
        int g = fc + num(bc - fc);
        int b = fc + num(bc - fc);
        return new Color(r, g, b);
    }

    private void drawHeart(Graphics2D g2d, int xn, int yn, CaptchaBgFillMode mode) {

        // t 代表弧度
        double t = 0;
        // vt 代表 t 的增量
        double vt = 0.01;
        // maxt 代表 t 的最大值
        double maxt = 2 * Math.PI;
        // 需要循环的次数
        double maxi = Math.ceil(maxt / vt);
        // 保存所有点的坐标的数组
        ArrayList<String> pointArr = new ArrayList();
        // 控制心形大小
        double size = 4/(1+num(4));
        // x 用来暂时保存每次循环得到的 x 坐标
        double x = 0;
        // y 用来暂时保存每次循环得到的 y 坐标
        double y = 0;

        if (CaptchaBgFillMode.PERFECT_HEART_SHAPE.equals(mode)){
            size /= 6;
        }

        for (double i = 0; i <= maxi; i++) {
            if (CaptchaBgFillMode.HOLLOW_DESCARTES_HEART_SHAPE.equals(mode)) {
                x = 2 * Math.sin(t) - Math.sin(2 * t);
                y = 2 * Math.cos(t) - Math.cos(2 * t);
            } else if (CaptchaBgFillMode.PERFECT_HEART_SHAPE.equals(mode)) {
                x = 16 * Math.pow(Math.sin(t), 3);
                y = 13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t);
            } else {
                return;
            }

            t += vt;
            pointArr.add(String.valueOf(x * size) + ',' + String.valueOf(-y * size));
        }

        for (String p : pointArr) {
            String[] arr = p.split(",");
            int x1 = (int) Double.parseDouble(arr[0]) + xn;
            int y1 = (int) Double.parseDouble(arr[1]) + yn;
            g2d.drawLine(x1, y1, x1, y1);

        }
    }

    /**
     * 验证码输出,抽象方法,由子类实现
     *
     * @param os 输出流
     */
    public abstract void out(OutputStream os);

    /**
     * 获取随机字符串
     *
     * @return string
     */
    public String text() {
        return chars;
    }

    public Font getFont() {
        return font;
    }

    public void setFont(Font font) {
        this.font = font;
    }

    public int getLen() {
        return len;
    }

    public void setLen(int len) {
        this.len = len;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public CaptchaBgFillMode getMode() {
        return mode;
    }

    public void setMode(CaptchaBgFillMode mode) {
        this.mode = mode;
    }
}

CaptchaBgFillMode.java

定义背景模式

package ranpic;

public enum CaptchaBgFillMode {

    ROUND("1"),
    HOLLOW_SHAPE("2"),
    PROTRUDING_STEREO_SHAPE("3"),
    CONCAVE_STEREOSCOPIC_SHAPE("4"),
    HOLLOW_DESCARTES_HEART_SHAPE("5"),
    PERFECT_HEART_SHAPE("6");


    CaptchaBgFillMode(String name) {
    }

}


PngCaptcha.java

PNG格式验证码实现

package ranpic;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;

import static ranpic.Randoms.num;

/**
 * <p>制作png静态图片</p>
 */
public class PngCaptcha extends Captcha {
    {
    }

    public PngCaptcha(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public PngCaptcha(int width, int height, int len) {
        this(width, height);
        this.len = len;
    }

    public PngCaptcha(int width, int height, int len, Font font) {
        this(width, height, len);
        this.font = font;
    }

    /**
     * 生成验证码
     *
     * @throws java.io.IOException IO异常
     */
    @Override
    public void out(OutputStream out) {
        graphicsImage(alphas(), out);
    }

    /**
     * 画随机码图
     *
     * @param strs 文本
     * @param out  输出流
     */
    private boolean graphicsImage(char[] strs, OutputStream out) {
        boolean ok = false;
        try {
            BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = (Graphics2D) bi.getGraphics();
            AlphaComposite ac3;
            Color color;
            int len = strs.length;
            g.setColor(Color.WHITE);
            g.fillRect(0, 0, width, height);
            // 随机画干扰的背景
            for (int i = 0; i < 15; i++) {
                color = color(150, 250);
                g.setColor(color);
                drawBgByMode(g, mode);
            }
            g.setFont(font);
            int h = height - ((height - font.getSize()) >> 1),
                    w = width / len,
                    size = w - font.getSize() + 1;
            /* 画字符串 */
            for (int i = 0; i < len; i++) {
                ac3 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f);// 指定透明度
                g.setComposite(ac3);
                color = new Color(20 + num(110), 20 + num(110), 20 + num(110));// 对每个字符都用随机颜色
                g.setColor(color);
                g.drawString(strs[i] + "", (width - (len - i) * w) + size, h - 4);
            }
            ImageIO.write(bi, "png", out);
            out.flush();
            ok = true;
        } catch (IOException e) {
            ok = false;
        } finally {
            Streams.close(out);
        }
        return ok;
    }
}


GifCaptcha.java

GIF格式验证码实现

package ranpic;

import tools.GifEncoder;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.OutputStream;

import static ranpic.Randoms.num;

public class GifCaptcha extends Captcha
{
    public GifCaptcha()
    {
    }

    public GifCaptcha(int width,int height){
        this.width = width;
        this.height = height;
    }

    public GifCaptcha(int width,int height,int len){
        this(width,height);
        this.len = len;
    }

    public GifCaptcha(int width, int height, int len, Font font)
    {
        this(width,height,len);
        this.font = font;
    }

    @Override
    public void out(OutputStream os)
    {
        try
        {
            GifEncoder gifEncoder = new GifEncoder(); 
            gifEncoder.start(os);
            gifEncoder.setQuality(180);
            gifEncoder.setDelay(100);
            gifEncoder.setRepeat(0);
            BufferedImage frame;
            char[] rands =alphas();
            Color fontcolor[]=new Color[len];
            for(int i=0;i<len;i++)
            {
                fontcolor[i]=new Color(20 + num(110), 20 + num(110), 20 + num(110));
            }
            for(int i=0;i<len;i++)
            {
                frame=graphicsImage(fontcolor, rands, i);
                gifEncoder.addFrame(frame);
                frame.flush();
            }
            gifEncoder.finish();
        }finally
        {
            Streams.close(os);
        }

    }

    /**
     * 画随机码图
     * @param fontcolor 随机字体颜色
     * @param strs 字符数组
     * @param flag 透明度使用
     * @return BufferedImage
     */
    private BufferedImage graphicsImage(Color[] fontcolor,char[] strs,int flag)
    {
        BufferedImage image = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
        //或得图形上下文
        //Graphics2D g2d=image.createGraphics();
        Graphics2D g2d = (Graphics2D)image.getGraphics();
        //利用指定颜色填充背景
        g2d.setColor(Color.WHITE);
        g2d.fillRect(0, 0, width, height);
        AlphaComposite ac3;

        int h  = height - ((height - font.getSize()) >>1) ;
        int w = width/len;
        g2d.setFont(font);

        for(int i=0;i<len;i++)
        {
            ac3 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getAlpha(flag, i));
            g2d.setComposite(ac3);
            g2d.setColor(fontcolor[i]);
            drawBgByMode(g2d, mode);
            g2d.drawString(strs[i]+"", (width-(len-i)*w)+(w-font.getSize())+1, h-4);
        }
        g2d.dispose();
        return image;
    }

    /**
     * 获取透明度,从0到1,自动计算步长
     * @return float 透明度
     */
    private float getAlpha(int i,int j)
    {
        int num = i+j;
        float r = (float)1/len,s = (len+1) * r;
        return num > len ? (num *r - s) : num * r;
    }

}

Streams.java

统一流关闭

package ranpic;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class Streams {
    /**
     * 关闭输入流
     *
     * @param in 输入流
     */
    public static void close(InputStream in) {
        if (in != null) {
            try {
                in.close();
            } catch (IOException ioex) {
                // ignore
            }
        }
    }

    /**
     * 关闭输出流
     *
     * @param out 输出流
     */
    public static void close(OutputStream out) {
        if (out != null) {
            try {
                out.flush();
            } catch (IOException ioex) {
                // ignore
            }
            try {
                out.close();
            } catch (IOException ioex) {
                // ignore
            }
        }
    }
}

测试

package ranpic;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;

public class Test {

    public static void main(String[] args) throws FileNotFoundException {
        Captcha captcha = new PngCaptcha(150, 40, 5);// png格式验证码
        captcha.out(new FileOutputStream("E:/1_1.png"));
        captcha.setMode(CaptchaBgFillMode.HOLLOW_SHAPE);
        captcha.out(new FileOutputStream("E:/1_2.png"));
        captcha.setMode(CaptchaBgFillMode.PROTRUDING_STEREO_SHAPE);
        captcha.out(new FileOutputStream("E:/1_3.png"));
        captcha.setMode(CaptchaBgFillMode.CONCAVE_STEREOSCOPIC_SHAPE);
        captcha.out(new FileOutputStream("E:/1_4.png"));
        captcha.setMode(CaptchaBgFillMode.HOLLOW_DESCARTES_HEART_SHAPE);
        captcha.out(new FileOutputStream("E:/1_5.png"));
        captcha.setMode(CaptchaBgFillMode.PERFECT_HEART_SHAPE);
        captcha.out(new FileOutputStream("E:/1_6.png"));


        //new Font("防宋", Font.PLAIN| Font.BOLD, 28)

        Captcha captcha1 = new GifCaptcha(150, 40, 5);//   gif格式动画验证码
        captcha1.out(new FileOutputStream("E:/2_1.gif"));
        captcha1.setMode(CaptchaBgFillMode.HOLLOW_SHAPE);
        captcha1.out(new FileOutputStream("E:/2_2.gif"));
        captcha1.setMode(CaptchaBgFillMode.PROTRUDING_STEREO_SHAPE);
        captcha1.out(new FileOutputStream("E:/2_3.gif"));
        captcha1.setMode(CaptchaBgFillMode.CONCAVE_STEREOSCOPIC_SHAPE);
        captcha1.out(new FileOutputStream("E:/2_4.gif"));
        captcha1.setMode(CaptchaBgFillMode.HOLLOW_DESCARTES_HEART_SHAPE);
        captcha1.out(new FileOutputStream("E:/2_5.gif"));
        captcha1.setMode(CaptchaBgFillMode.PERFECT_HEART_SHAPE);
        captcha1.out(new FileOutputStream("E:/2_6.gif"));

    }
}

最后附带GIF图片生成工具类
tools.rar

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值