吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2059|回复: 0
收起左侧

[Java 转载] 【转载】Java实现验证码的制作

[复制链接]
cinami 发表于 2021-1-15 23:20
验证码概述
为什么使用验证码?
  验证码(CAPTCHA)是一种全自动程序。主要是为了区分“进行操作的是不是人”。如果没有验证码机制,将会导致以下的问题:

  • 对特定网站不断进行登录,破解密码;
  • 对某个网站创建账户;
  • 对某个网站提交垃圾数据(灌水贴);
  • 对某个网站进行刷票。



使用Servlet实现验证码
  一个验证码包含两个部分:图片和输入框。
[Java] 纯文本查看 复制代码
<script type="text/javascript">
    function reloadCode(){
        var time = new Date();
        // 给URL传递参数可以清空浏览器的缓存,让浏览器认为这是一个新的请求
        document.getElementById('safecode').src = '<%=request.getContextPath()%>/servlet/ImageServlet?d=' + time;
    }
</script>

<form action="<%=request.getContextPath()%>/servlet/ValidateImageServlet"method="post">
        验证码:<img src="<%=request.getContextPath()%>/servlet/ImageServlet" alt="验证码" id="safecode">
    <input type="text" id="verifyCode" name="verifyCode" size="6" />
    <a href="javascript:reloadCode();">看不清楚</a><br>
    <input type="submit" value="登录" />
</form>

我们用ImageServlet实时生成图片。生成图片所需要的步骤如下:
  • 1 定义BufferedImage对象
  • 2 获得Graphics对象
  • 3 听过Random类产生随机验证码信息
  • 4 使用Graphics绘制图片
  • 5 记录验证码信息到session中
  • 6 使用ImageIO输出图片

检验验证码是否正确:ValidateImageServlet
  • 1 获取页面的验证码
  • 2 获取session中保存的验证码
  • 3 比较验证码
  • 4 返回校验结果

验证的流程如下:

生成验证码的ImageServlet:
[Java] 纯文本查看 复制代码
private static Random r = new Random();
    private static char[] chs = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".toCharArray();
    private static final int NUMBER_OF_CHS = 4;
    private static final int IMG_WIDTH = 65;
    private static final int IMG_HEIGHT = 25;


    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

            BufferedImage image = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, BufferedImage.TYPE_INT_RGB);    // 实例化BufferedImage
            Graphics g = image.getGraphics();
            Color c = new Color(200, 200, 255);                                             // 验证码图片的背景颜色
            g.setColor(c);
            g.fillRect(0, 0, IMG_WIDTH, IMG_HEIGHT);                                        // 图片的边框

            StringBuffer sb = new StringBuffer();                                           // 用于保存验证码字符串
            int index;                                                                      // 数组的下标
            for (int i = 0; i < NUMBER_OF_CHS; i++) {
                index = r.nextInt(chs.length);                                              // 随机一个下标
                g.setColor(new Color(r.nextInt(88), r.nextInt(210), r.nextInt(150)));       // 随机一个颜色
                g.drawString(chs[index] + "", 15 * i + 3, 18);                              // 画出字符
                sb.append(chs[index]);                                                      // 验证码字符串
            }

            request.getSession().setAttribute("piccode", sb.toString());                    // 将验证码字符串保存到session中
            ImageIO.write(image, "jpg", response.getOutputStream());                        // 向页面输出图像
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}

进行验证码图片验证的Servlet:
[Java] 纯文本查看 复制代码
public class ValidateImageServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        doPost(request, response);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType("text/html;charset=utf-8");
        String picString = (String) request.getSession().getAttribute("piccode");
        String checkCode = request.getParameter("verifyCode");
        PrintWriter out = response.getWriter();
        if (picString.toUpperCase().equals(checkCode.toUpperCase()))
            out.println("验证码正确");
        else
            out.print("验证码错误!");

        out.flush();
        out.close();
    }

}

开源组件实现验证码

Jcaptcha:
  一个用来生成图形验证码的开源组件,可以产生多种形式的验证码。可以与Spring组合使用。需要导入的jar包如下:

 用于展示验证码的auth_code_captcha.jsp如下:
[Java] 纯文本查看 复制代码
 <form action="submit.action" method="post">
      <img src="jcaptcha.jpg" /> <input type="text" name="japtcha" value="" />
      <input type="submit"/>
 </form>

web.xml的配置如下:
[Java] 纯文本查看 复制代码
<servlet>
    <servlet-name>jcaptcha</servlet-name>
    <servlet-class>com.octo.captcha.module.servlet.image.SimpleImageCaptchaServlet</servlet-class>
</servlet>
<!-- 处理表单提交的Servlet -->
<servlet>
    <servlet-name>submit</servlet-name>
    <servlet-class>org.gpf.servlet.SubmitActionServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>jcaptcha</servlet-name>
    <url-pattern>/jcaptcha.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>submit</servlet-name>
    <url-pattern>/submit.action</url-pattern>
</servlet-mapping>

表单提交的Servlet:
[Java] 纯文本查看 复制代码
package org.gpf.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.octo.captcha.module.servlet.image.SimpleImageCaptchaServlet;
/**
 * 图片验证码的captcha实现
 * [url=home.php?mod=space&uid=686208]@AuThor[/url] gaopengfei
 * [url=home.php?mod=space&uid=686237]@date[/url] 2015-5-20 下午9:58:20
 */
public class SubmitActionServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        String userCaptchaResponse = request.getParameter("japtcha");
        boolean captchaPassed = SimpleImageCaptchaServlet.validateResponse(request, userCaptchaResponse);

        response.setContentType("text/html;charset=utf-8");
        if (captchaPassed)
            response.getWriter().write("验证通过!");
        else {
            response.getWriter().write("验证失败!");
        }
        response.getWriter().write("<br/><a href='auth_code_captcha.jsp'>重新验证</a>");
    }
}


kaptcha:
  它是可以配置的,也可以生成各种样式的验证码。如下是其简单应用:用于显示验证码的index.jsp
[Java] 纯文本查看 复制代码
<img alt="验证码图片" src="random.jpg">
 <form action="check.jsp" method="post">
     <input type="text" name="imageText">
     <input type="submit" value="验证">
 </form>

用于验证验证码的check.jsp
[Java] 纯文本查看 复制代码
<%[url=home.php?mod=space&uid=402414]@[/url] page import="com.google.code.kaptcha.Constants" %>
<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8"%>
<%
    String myImageText = request.getParameter("imageText");
    String key = (String)request.getSession().getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);

    if(!myImageText.isEmpty() && myImageText.equals(key))
        out.print("验证通过!<br />");
    else
        out.print("验证失败!<br />");
    out.print("你输入的字符:" + myImageText + ",验证码字符:" + key);
 %>

配置图片显示的Servlet:
[Java] 纯文本查看 复制代码
<servlet>
    <servlet-name>Kcaptcha</servlet-name>
    <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>Kcaptcha</servlet-name>
    <url-pattern>/random.jpg</url-pattern>
</servlet-mapping>


Kaptcha的详细配置
[Java] 纯文本查看 复制代码
<servlet>
    <servlet-name>Kcaptcha</servlet-name>
    <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>

    <!-- 边框 -->
    <init-param>
        <description>图片边框,yes(默认值)或者no</description>
        <param-name>kaptcha.border</param-name>
        <param-value>yes</param-value>
    </init-param>
    <init-param>
        <description>边框颜色,white、black(默认)、blue等</description>
        <param-name>kaptcha.border.color</param-name>
        <param-value>green</param-value>
    </init-param>
    <init-param>
        <description>边框厚度大于0</description>
        <param-name>kaptcha.border.thickness</param-name>
        <param-value>10</param-value>
    </init-param>

    <!-- 图片宽高 -->
    <init-param>
        <description>图片宽度</description>
        <param-name>kaptcha.image.width</param-name>
        <param-value>200</param-value>
    </init-param>
    <init-param>
        <description>图片高度</description>
        <param-name>kaptcha.image.height</param-name>
        <param-value>60</param-value>
    </init-param>

    <!-- 图片样式 -->
    <init-param>
        <description>图片样式:水纹(WaterRipple)、鱼眼(FishEyeGimpy)、阴影(ShadowGimpy)</description>
        <param-name>kaptcha.obscurificator.impl</param-name>
        <param-value>com.google.code.kaptcha.impl.ShadowGimpy</param-value>
    </init-param>

    <!-- 背景 -->
    <init-param>
        <description>背景实现类</description>
        <param-name>kaptcha.background.impl</param-name>
        <param-value>com.google.code.kaptcha.impl.DefaultBackground</param-value>
    </init-param>
    <init-param>
        <description>背景颜色渐变,指定开始颜色</description>
        <param-name>kaptcha.background.clear.from</param-name>
        <param-value>yellow</param-value>
    </init-param>
    <init-param>
        <description>背景颜色渐变,指定结束颜色</description>
        <param-name>kaptcha.background.clear.to</param-name>
        <param-value>red</param-value>
    </init-param>

    <!-- 文本 -->
    <init-param>
        <description>文本集合,验证码文字从此集合中获取</description>
        <param-name>kaptcha.textproducer.char.string</param-name>
        <param-value>0123456789</param-value>
    </init-param>
    <init-param>
        <description>验证码长度</description>
        <param-name>kaptcha.textproducer.char.length</param-name>
        <param-value>6</param-value>
    </init-param>
    <init-param>
        <description>文字间隔</description>
        <param-name>kaptcha.textproducer.char.space</param-name>
        <param-value>2</param-value>
    </init-param>
    <!-- 字体 -->
    <init-param>
        <description>字体Arial,Courier</description>
        <param-name>kaptcha.textproducer.font.names</param-name>
        <param-value>Arial,Courier</param-value>
    </init-param>
    <init-param>
        <description>字体大小</description>
        <param-name>kaptcha.textproducer.font.size</param-name>
        <param-value>40</param-value>
    </init-param>
    <init-param>
        <description>字体颜色,white、black(默认)、blue等</description>
        <param-name>kaptcha.textproducer.font.color</param-name>
        <param-value>pink</param-value>
    </init-param>

    <init-param>
        <description>文字渲染器</description>
        <param-name>kaptcha.word.impl</param-name>
        <param-value>com.google.code.kaptcha.text.impl.DefaultWordRenderer</param-value>
    </init-param>

    <!-- 图片和文本的实现类 -->
    <init-param>
        <description>图片实现类,可以重写这个类实现我们自己的图片</description>
        <param-name>kaptcha.producer.impl</param-name>
        <param-value>com.google.code.kaptcha.impl.DefaultKaptcha</param-value>
    </init-param>
    <init-param>
        <description>文本实现类</description>
        <param-name>kaptcha.textproducer.impl</param-name>
        <param-value>com.google.code.kaptcha.text.impl.DefaultTextCreator</param-value>
    </init-param>

    <!-- 干扰 -->
    <init-param>
        <description>干扰实现类</description>
        <param-name>kaptcha.noise.impl</param-name>
        <param-value>com.google.code.kaptcha.impl.DefaultNoise</param-value>
    </init-param>
    <init-param>
        <description>干扰颜色,合法值r,g,b或者white、black、blue</description>
        <param-name>kaptcha.noise.color</param-name>
        <param-value>255,0,0</param-value>
    </init-param>

    <init-param>
        <description>session中存放验证码的key键</description>
        <param-name>kaptcha.session.key</param-name>
        <param-value>KAPTCHA_SESSION_KEY</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>Kcaptcha</servlet-name>
    <url-pattern>/random.jpg</url-pattern>
</servlet-mapping>


查看前面的配置发现验证码字符的生成主要依靠的是kaptcha.textproducer.impl这个文本实现类,查看com.google.code.kaptcha.text.impl.DefaultTextCreator的源码,发现了它继承自 Configurable并实现了TextProducer接口。我们可以仿照它自定义我们自己的验证码中的文本生成器:
[Java] 纯文本查看 复制代码
/**
 * 中文验证码的实现类
 */
public class ChineseTextCreator extends Configurable implements TextProducer {

    @Override
    public String getText() {

        int length = getConfig().getTextProducerCharLength();
        String finalWord = "", firstWord = "";
        int tempInt = 0;
        String[] array = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
                "a", "b", "c", "d", "e", "f" };

        Random rand = new Random();

        for (int i = 0; i < length; i++) {
            switch (rand.nextInt(array.length)) {
            case 1:
                tempInt = rand.nextInt(26) + 65;
                firstWord = String.valueOf((char) tempInt);
                break;
            case 2:
                int r1,
                r2,
                r3,
                r4;
                String strH,
                strL;// high&low
                r1 = rand.nextInt(3) + 11; // 前闭后开[11,14)
                if (r1 == 13) {
                    r2 = rand.nextInt(7);
                } else {
                    r2 = rand.nextInt(16);
                }

                r3 = rand.nextInt(6) + 10;
                if (r3 == 10) {
                    r4 = rand.nextInt(15) + 1;
                } else if (r3 == 15) {
                    r4 = rand.nextInt(15);
                } else {
                    r4 = rand.nextInt(16);
                }

                strH = array[r1] + array[r2];
                strL = array[r3] + array[r4];

                byte[] bytes = new byte[2];
                bytes[0] = (byte) (Integer.parseInt(strH, 16));
                bytes[1] = (byte) (Integer.parseInt(strL, 16));

                firstWord = new String(bytes);
                break;
            default:
                tempInt = rand.nextInt(10) + 48;
                firstWord = String.valueOf((char) tempInt);
                break;
            }
            finalWord += firstWord;
        }
        return finalWord;
    }
}

只需要在web.xml中将初始化参数由默认的文本实现类改成我们自己的实现类:
[Java] 纯文本查看 复制代码
<init-param>
     <description>文本实现类</description>
     <param-name>kaptcha.textproducer.impl</param-name>
     <param-value>ChineseTextCreator</param-value>
 </init-param>


算式验证码
实现步骤如下:
  • 获取随机的数值将结果相加
  • 将计算公式写入到验证码图片
  • 将相加的结果放入到session中

因此,我们需要重写KaptchaServlet这个用于生成验证码的Servlet。
[Java] 纯文本查看 复制代码
public class MyKaptchaServlet extends HttpServlet implements Servlet{

    private Properties props;
    private Producer kaptchaProducer;
    private String sessionKeyValue;

    public MyKaptchaServlet() {
         props = new Properties();
         kaptchaProducer = null;
         sessionKeyValue = null;
    }

    public void init(ServletConfig conf) throws ServletException {
        super.init(conf);

        ImageIO.setUseCache(false);

        Enumeration initParams = conf.getInitParameterNames();
        while (initParams.hasMoreElements()) {
            String key = (String) initParams.nextElement();
            String value = conf.getInitParameter(key);
            this.props.put(key, value);
        }

        Config config = new Config(this.props);
        this.kaptchaProducer = config.getProducerImpl();
        this.sessionKeyValue = config.getSessionKey();
    }

    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setDateHeader("Expires", 0L);

        resp.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");

        resp.addHeader("Cache-Control", "post-check=0, pre-check=0");

        resp.setHeader("Pragma", "no-cache");

        resp.setContentType("image/jpeg");

        String capText = this.kaptchaProducer.createText();
        String s1 = capText.substring(0, 1);    // 获取随机生成的第一个数字
        String s2 = capText.substring(1, 2);    // 由于web.xml中配置的验证码字符都是数字,不会发生数字格式化异常
        int r = Integer.parseInt(s1) + Integer.parseInt(s2);

        req.getSession().setAttribute(this.sessionKeyValue, String.valueOf(r));    // 将结果存入session

        BufferedImage bi = this.kaptchaProducer.createImage(s1 + " + " + s2 + " = ?"); // 产生图片

        ServletOutputStream out = resp.getOutputStream();

        ImageIO.write(bi, "jpg", out);
        try {
            out.flush();
        } finally {
            out.close();
        }
    }

}

在web.xml中有2点需要注意:
  • 验证码文本只能是0~9这10个数字;
  • 验证码的长度是2(写多了也只会取出前2个随机数,写少了会抛出数字格式化异常)。

[Java] 纯文本查看 复制代码
<servlet-name>Kcaptcha</servlet-name>
    <servlet-class>MyKaptchaServlet</servlet-class>
<init-param>
        <description>文本集合</description>
        <param-name>kaptcha.textproducer.char.string</param-name>
        <param-value>0123456789</param-value>
    </init-param>
    <init-param>
    <description>验证码长度</description>
    <param-name>kaptcha.textproducer.char.length</param-name>
    <param-value>8</param-value>
</init-param>

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-25 20:40

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表