【Web】Java生成中文GIF动态验证码-集成SpringMVC

转载请注明出处:http://blog.csdn.net/qq_26525215

本文源自大学之旅_谙忆的博客

说明

GIF验证码相对于JPG图片验证码来说,要更难破解一些,加大了破解的代价。

从昨天到现在,写了一个小小的GIF验证码项目(中文成语)。

当然,你可以自己修改成字母数字的。我只是单纯的觉得中文验证码的破解代价更高一点~

我在这里生成GIF图片的类,用到了国外牛人的三个类,也就是:

AnimatedGifEncoder

LZWEncoder

和NeuQuant,这三个类。

没办法,谁让自己还没有那个本事写出这样的类呢,只能用别人的,不过挺好用飞,大家可以搜索一下这3个类,一下就能搜出源码的。

在这里,我就不贴出这三个类的源码了,需要的,可以在本文最后的项目链接拿整个项目,其中有所有源代码。

本来一开始是写的字母和数字生成的GIF验证码,后来还是改成了汉字成语验证码。

在这里,我并没有用数据库来存储成语,因为重点不在哪里,所以就只是建立了一个静态块来先写入成语。

(如果是实际开发,我可能会这样做:

以便于管理员在后台可以添加成语到验证码成语库,以及可以刷新验证码到成语库中,所以,可以在一个请求方法中操作成语。

如果用来Redis,基本上也是一样,实现同步就行。)

GIF验证码类

package cn.hncu.utils;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * User: 陈浩翔.
 * Date: 2017/3/6.
 * Time: 下午 8:23.
 * Explain:Gif验证码类
 */
public class GifCaptcha {
    private Font font = new Font("宋体", Font.BOLD, 20); // 字体
    private int width = 160; // 验证码显示长度
    private int height = 40; // 验证码显示高度
    private String word = ""; // 当前的字符串
    private int delay = 100; // 帧延迟 (默认100)
    private int quality = 10;//量化器取样间隔 - 默认是10ms
    private int repeat = 0; // 帧循环次数
    private int minColor =0;//设置随机颜色时,最小的取色范围
    private int maxColor = 255;//设置随机颜色时,最大的取色范围
    private int right = 0; //设置字符最右边的相对位置---相对原始位置 ,默认为0
    private static java.util.List<String> words = new ArrayList<String>();// 所有成语

    //这里应该去数据库中读取成语,然后存储在内存中
    //在实际开发中,应该是可以在后台中添加成语,以及刷新成语到内存中去!利用访问某个方法来实现
    static {
        words.add("一唱一和");
        words.add("一呼百应");
        words.add("一干二净");
        words.add("一举两得");
        words.add("一落千丈");
        words.add("两面三刀");
        words.add("六神无主");
        words.add("千辛万苦");
        words.add("万无一失");
        words.add("拔刀相助");
        words.add("过时黄花");
        words.add("地动山摇");
        words.add("不可多得");
        words.add("沧海一粟");
        words.add("水泄不通");
        words.add("不可计数");
    }

    /**
     * 空参构造函数
     */
    public GifCaptcha() {
    }

    /**
     * 可以设置验证码宽度,高度的构造函数
     * @param width -验证码宽度
     * @param height -验证码高度
     */
    public GifCaptcha(int width, int height) {
        this.width = width;
        this.height = height;
    }
    /**
     *
     * @param width -验证码宽度
     * @param height -验证码高度
     * @param font -字体
     */
    public GifCaptcha(int width, int height,  Font font) {
        this(width, height);
        this.font = font;
    }

    /**
     * @param width -验证码宽度
     * @param height -验证码高度
     * @param font -字体
     * @param delay -帧延迟
     */
    public GifCaptcha(int width, int height, Font font,int delay) {
        this(width, height,font);
        this.delay = delay;
    }

    public Font getFont() {
        return font;
    }

    /**
     * 设置字体
     * @param font
     */
    public void setFont(Font font) {
        this.font = font;
    }

    public int getWidth() {
        return width;
    }
    /**
     * 设置验证码宽度
     * @param width
     */
    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    /**
     * 设置验证码高度
     * @param height
     */
    public void setHeight(int height) {
        this.height = height;
    }

    public String getWord() {
        return word;
    }

    /**
     * 设置验证码字符
     * @param chars
     */
    public void setWord(String chars) {
        this.word = chars;
    }

    public int getDelay() {
        return delay;
    }

    /**
     * 设置每一帧之间的延迟时间,或改变它的后续帧(适用于最后一帧添加)。
     * @param delay 单位是毫秒
     */
    public void setDelay(int delay) {
        this.delay = delay;
    }

    public int getQuality() {
        return quality;
    }

    /**
     * 设置图像的颜色量化(转换质量 由GIF规范允许的最大256种颜色)。
     * 低的值(最小值= 1)产生更好的颜色,但处理显著缓慢。
     * 10是默认,并产生良好的颜色而且有以合理的速度。
     * 值更大(大于20)不产生显著的改善速度
     * @param quality 大于1
     */
    public void setQuality(int quality) {
        if(quality<1){
            quality=1;
        }
        this.quality = quality;
    }

    public int getRepeat() {
        return repeat;
    }

    /**
     * 设置GIF帧应该播放的次数。
     * 默认是 0; 0意味着无限循环。
     * 必须在添加的第一个图像之前被调用。
     * @param repeat 必须大于等于0
     */
    public void setRepeat(int repeat) {
        if (repeat>=0) {
            this.repeat = repeat;
        }
    }

    public int getRight() {
        return right;
    }

    public void setRight(int right) {
        this.right = right;
    }

    public int getMaxColor() {
        return maxColor;
    }

    public void setMaxColor(int maxColor) {
        this.maxColor = maxColor;
    }

    public int getMinColor() {
        return minColor;
    }

    public void setMinColor(int minColor) {
        this.minColor = minColor;
    }

    /**
     * 给定一个输出流 输入图片
     * @param os
     */
    public void out(OutputStream os) {
        try {
            AnimatedGifEncoder gifEncoder = new AnimatedGifEncoder();// gif编码类
            //生成字符
            gifEncoder.start(os);
            gifEncoder.setQuality(quality);//设置量化器取样间隔
            gifEncoder.setDelay(delay);//设置帧延迟
            gifEncoder.setRepeat(repeat);//帧循环次数
            BufferedImage frame;
            char[] rands = createWordChar();
            Color fontcolor[] = new Color[word.length()];
            for (int i = 0; i < word.length(); i++) {
                fontcolor[i] = getRandomColor(minColor,maxColor);
            }
            for (int i = 0; i < word.length(); i++) {
                frame = graphicsImage(fontcolor, rands, i);
                gifEncoder.addFrame(frame);
                frame.flush();
            }
            gifEncoder.finish();
        } finally {
            try {
                os.close();
            } catch (IOException e) {
                // TODO 异常处理
                e.printStackTrace();
            }
        }

    }

    /**
     * 画随机码图
     *
     * @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 ac;
        float y = (height >> 1) + (font.getSize() >> 1) ;// 字符的y坐标
        float m = (width-(word.length()*font.getSize()))/word.length();
        float x = m/2;//字符的x坐标
        g2d.setFont(font);
        for (int i = 0; i < word.length(); i++) {
            ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getPellucidity(flag, i));
            g2d.setComposite(ac);
            g2d.setColor(fontcolor[i]);
            g2d.drawOval(Randoms.num(width), Randoms.num(height), Randoms.num(5,30), 5 + Randoms.num(5,30));//绘制椭圆边框
            g2d.drawString(strs[i] + "",x+(font.getSize()+m)*i+right,y);
        }
        g2d.dispose();
        return image;
    }

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

    /**
     * 生成随机字符数组
     * @return 字符数组
     */
    protected char[] createWordChar() {
        word = words.get(Randoms.num(words.size()));
        return word.toCharArray();
    }

    /**
     * 通过给定范围获得随机的颜色
     * @return Color 获得随机的颜色
     */
    protected Color getRandomColor(int min, int max) {
        if (min > 255) {
            min = 255;
        }
        if (max > 255) {
            max = 255;
        }
        if(min<0){
            min=0;
        }
        if(max<0){
            max=0;
        }
        if(min>max){
            min=0;
            max=255;
        }
        return new Color(min + Randoms.num(max - min), min + Randoms.num(max - min), min + Randoms.num(max - min));
    }
}

注释没写很多~有点懒~

请求GIF验证码的Controller类

package cn.hncu.controller;

import cn.hncu.utils.GifCaptcha;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.*;
import java.io.IOException;

/**
 * Created with IntelliJ IDEA.
 * User: 陈浩翔.
 * Date: 2017/3/6.
 * Time: 下午 8:26.
 * Explain:演示GIF验证码的控制器
 */
@Controller
public class CaptchaController {
    private Logger logger = LoggerFactory.getLogger(CaptchaController.class);
    /**
     * 获取Gif验证码
     * @param response
     */
    @RequestMapping(value = "gifCaptcha",method= RequestMethod.GET)
    public void getGifCaptcha(HttpServletResponse response,HttpServletRequest request){
        //告诉客户端,输出的格式
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        response.setContentType("image/gif");
        GifCaptcha gifCaptcha =  new GifCaptcha(200,80,new Font("宋体", Font.BOLD, 40),100);
        try {
            gifCaptcha.out(response.getOutputStream());
            logger.info("获取验证码!验证码字符为:"+gifCaptcha.getWord());
            HttpSession session = request.getSession(true);
            //存入Session
            session.setAttribute("captchaWord",gifCaptcha.getWord());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @RequestMapping("index")
    public String index()    {
        return "index";
    }

}

JSP页面

<%--
  Created by IntelliJ IDEA.
  User: 陈浩翔
  Date: 2017/3/6
  Time: 下午 8:24
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>演示动态验证码</title>
    <script type="text/javascript">
        var path = "${pageScope.basePath}";
        function changImg() {
            var img = document.getElementById("servletImg");
            var d = new Date();
            var time = d.getTime();//如果没有这个,下面的img.src = path + "gifCaptcha?" + time;不会起作用,因为浏览器的缓存技术,图片可能并不会刷新

            img.src = "";//解决火狐下验证码刷不出的问题
            img.src = path + "gifCaptcha?" + time;
            //?号后面的东西是通过get方式传递的
        }
    </script>
</head>
<body>
演示动态验证码:
<a onclick="javascript:changImg();" href="javascript:void(0);">
    <img id="servletImg" src="gifCaptcha" alt="UIFuture验证码"/>
</a>
</body>
</html>

演示效果

大家其实可以看到,在我点击验证码的时候,有一个小停顿,会显示alt的内容,那是因为我在JS中,2次赋值给img的src属性。

原因是为了解决火狐浏览器显示GIF图的一个问题,如果我不加那个img.src = “”;,在刷新验证码2次后,验证码gif图只显示第一帧!也就是变成了静态图~但是接收到的图片其实还是GIF动图。

我加img.src = “”;,就只是为了解决火狐上验证码刷新2次后会变成静图的问题,该问题在谷歌浏览器,以及360浏览器上没有出现!

有知道原因的请评论,谢谢

出问题的是下面这样的情况,在第三次点击图片刷新时(此时用的是同一张图片,随机图片出现的问题是一样的,也就是只显示GIF动图的第一帧图片)(火狐浏览器)

谷歌浏览器,360浏览器没有出现该问题。

本篇博客涉及到的源码链接:

->点击访问源码-?CHX

本文章由[谙忆]编写, 所有权利保留。

欢迎转载,分享是进步的源泉。

转载请注明出处:http://blog.csdn.net/qq_26525215

本文源自大学之旅_谙忆的博客

时间: 2024-07-30 23:52:06

【Web】Java生成中文GIF动态验证码-集成SpringMVC的相关文章

Java生成中文汉字随机验证码

实现主要包含两个类,一个是生成验证码,一个是判断验证码输入是否正确 实现原理比较简单,将汉字和干扰线生成图片并将汉字保存到session,前台获取每次生成的验证码图片进行展示,验证的时候就获取文本框输入的值传到后台与session值进行比较,功能就怎么简单. 一.生成汉字验证码的类 package com.veryCode; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt

Java 生成任意长度的验证码的两个方法

|--需求说明 1.要求生成任意长度的验证码 2.验证码要求包含大小写英文字母和数字 |--实现方式 采用随机数的方式,分别在数字,大小写英文字母里面抽取字符,抽取次数由for循环控制 |--代码内容 1 package com.work.work3; 2 3 /** 4 * @auther::9527 5 * @Description: 验证码生成器 6 * @program: shi_yong 7 * @create: 2019-07-30 20:45 8 */ 9 public class

Linux使用ssh动态验证码登录机器

ssh动态验证码登录机器 Google Authenticator是一个动态验证码程序,兼容各种智能手机平板设备,可以用来做各种帐号的二次验证,增加帐号的安全性.SSH是Linux系统的最重要防线之一,为了防止密码泄露或者被爆破,可以使用Google Authenticator来做二次验证,使用方法也很简单 谷歌身份验证器生成的是动态验证码,默认30秒更新.修改配置,SSH登录必须在输入密码之前输入动态验证码.即使账号和密码泄露,验证码输入错误,仍然无法登录.苹果或者安卓手机端可以安装身份验证器

Java Web:使用Servlet生成网页随机图片验证码

最近在学习Java Web开发,做了一个生成网页随机图片验证码的例子,在此记录. 一.新建Servlet项目: 在MyEclipse中新建Servlet项目,一步步操作就OK,在此不再赘述.建好之后文件目录树如下图: 二.源代码实现: (1)java代码: package com.zdt.identity; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.image.

Servlet仿CSDN动态验证码的生成-带数字和字母

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 一.实现的思路: (1)首先,须要创建一个Servlet.该Servlet通过字节型响应给client返回一个图片.该图片是通过JDK中Java 2D的类库来生成一个图片. 图片的生成是依靠一个随机数来完毕,然后将这个随机数写成图片格式.最后在Session将这个随机的字符串的状态保持住,以便在用户填写后进行对照. (2)其次,在须要加入验证码的JSP页面中,通过<img src="生

Servlet动态验证码的生成-带数字和字母

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 一.实现的思路: (1)首先,需要创建一个Servlet.该Servlet通过字节型响应给客户端返回一个图片,该图片是通过JDK中Java 2D的类库来生成一个图片.图片的生成是依靠一个随机数来完成,然后将这个随机数写成图片格式.最后在Session将这个随机的字符串的状态保持住,以便在用户填写后进行对比. (2)其次,在需要加入验证码的JSP页面中,通过<img src="生成验证码

Flask 生成中文图片验证码

因最近要用到验证码,上网搜了下,发现什么验证码感觉都能被攻破,连最近疯传的变态的12306的验证码居然有人一天就攻破了,所以,综合考虑,还是使用汉字: web框架是Flask,然后使用python的Image库生成中文验证码,后续也可加入数字字母啥的. 代码如下: # -*- coding: utf-8 -*- import sys reload(sys) sys.setdefaultencoding('utf-8') """ __author__="tina&qu

Java生成验证码(包含gif动画验证码)

1.验证码抽象类import java.awt.*;import java.io.OutputStream; import static Randoms.num;import static Randoms.alpha; * <p>验证码抽象类,暂时不支持中文</p> * @author: wuhongjun * @version:1.0public abstract class Captcha protected Font font = new Font("Verdana

java生成验证码

一:需求分析 使用java生成验证码: 1:生成画布,画好背景图 2:画随机数 3:画干扰线 4:将内存中的图片保存到硬盘上 二:代码如下 1 /** 2 * 3 */ 4 package com.hlcui.io; 5 6 import java.awt.Color; 7 import java.awt.Font; 8 import java.awt.Graphics; 9 import java.awt.image.BufferedImage; 10 import java.io.File;