JAVA实用案例之验证码开发

验证码在很多地方都会遇到,实现的方法和形式也有很多,主要的目的就是为了安全,防止一些恶意的攻击等。说实话那么多年竟然没注意过这东西,原理很简单,贴出来给大家做个参考。

1、简单介绍

一般稍微有些经验的程序员都不会再自己写原生验证码生成了,因为各种强大的开源组件,足以解决我们大部分的需求。但是,毕竟也是刚接触这东西,还需要从原理入手的。

项目效果图:

下面我就简单介绍下原生和使用开源项目kaptcha生成验证码的两种形式。

2、jdk原生生成验证码

效果:

2.1 验证码生成的流程

1、定义BufferedImage(图像数据缓冲区)对象

2、获得Graphics对象

3、随机生成验证码字母或者数字

4、使用Graphics绘制图片

5、记录验证码信息到session或数据库,以便校验

5、ImageIO输出图片到客户端

2.2 代码讲解

这里我就不整合框架了,简单用servlet讲下步骤,框架中也是这样做

public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException{
        //定义BufferedImage(图像数据缓冲区)对象
        BufferedImage bi = new BufferedImage(68,22,BufferedImage.TYPE_INT_RGB);
        //绘制图片
        Graphics g = bi.getGraphics();
        //背景色
        Color c = new Color(200,150,255);
        g.setColor(c);
        //图片坐标
        g.fillRect(0, 0, 68, 22);
        //验证码选取
        char[] ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
        Random r = new Random();
        int len=ch.length,index;
        StringBuffer sb = new StringBuffer();
        for(int i=0; i<4; i++){
            index = r.nextInt(len);
            g.setColor(new Color(r.nextInt(88),r.nextInt(188),r.nextInt(255)));
            Font ft = new Font(Font.SANS_SERIF, Font.BOLD, 16);
            g.setFont(ft);
            g.drawString(ch[index]+"", (i*15)+3, 18);
            sb.append(ch[index]);
        }
//打印验证码,项目中用日志 System.out.println(sb.toString());
     //验证码写到session request.getSession().setAttribute(
"checkCode", sb.toString()); //ImageIO写出图片 ImageIO.write(bi, "JPG", response.getOutputStream()); }

jsp:

 <form action="XX" method="get">
    验证码:<input type="text" name="checkcode"/>
    <img alt="点击更换验证码" id="imagecode" onclick="this.src='/servlet/ImageServlet?random='+Math.random();" src="/servlet/ImageServlet"/>
    <input type="submit" value="提交">
  </form>

 相信稍微有些经验的同学看过上面的代码都能理解其中的原理吧。至于后面的校验相信大家都会的。

下面我重点讲下使用kaptcha开源组件生成验证码的流程,这里我会用servlet和springboot+springmvc的方式分别进行下介绍。

3、使用kaptcha组件生成验证码

既然说到开源组件,必然功能是强大的,还是先看效果图!

数字字母组合

数字字母汉字组合

算数计算

3.1 kaptcha的参数详解

Constant 描述 默认值
kaptcha.border 图片边框,合法值:yes , no yes
kaptcha.border.color 边框颜色,合法值: r,g,b (and optional alpha) 或者 white,black,blue. black
kaptcha.border.thickness 边框厚度,合法值:>0 1
kaptcha.image.width 图片宽 200
kaptcha.image.height 图片高 50
kaptcha.producer.impl 图片实现类 com.google.code.kaptcha.impl.DefaultKaptcha
kaptcha.textproducer.impl 文本实现类 com.google.code.kaptcha.text.impl.DefaultTextCreator
kaptcha.textproducer.char.string 文本集合,验证码值从此集合中获取 abcde2345678gfynmnpwx
kaptcha.textproducer.char.length 验证码长度 5
kaptcha.textproducer.font.names 字体 Arial, Courier
kaptcha.textproducer.font.size 字体大小 40px
kaptcha.textproducer.font.color 字体颜色,合法值: r,g,b  或者 white,black,blue. black
kaptcha.textproducer.char.space 文字间隔 2
kaptcha.noise.impl 干扰实现类 com.google.code.kaptcha.impl.DefaultNoise
kaptcha.noise.color 干扰颜色,合法值: r,g,b 或者 white,black,blue. black
kaptcha.obscurificator.impl 图片样式:
水纹com.google.code.kaptcha.impl.WaterRipple
鱼眼com.google.code.kaptcha.impl.FishEyeGimpy
阴影com.google.code.kaptcha.impl.ShadowGimpy
com.google.code.kaptcha.impl.WaterRipple
kaptcha.background.impl 背景实现类 com.google.code.kaptcha.impl.DefaultBackground
kaptcha.background.clear.from 背景颜色渐变,开始颜色 light grey
kaptcha.background.clear.to 背景颜色渐变,结束颜色 white
kaptcha.word.impl 文字渲染器 com.google.code.kaptcha.text.impl.DefaultWordRenderer
kaptcha.session.key session key KAPTCHA_SESSION_KEY
kaptcha.session.date session date KAPTCHA_SESSION_DATE

3.2 代码讲解

1、servlet方式

上图可以看到,kaptcha处理验证码的类是KaptchaServlet。这里我们就可以像原生的方式一样直接请求这个servlet,这里主要讲下使用servlet和使用框架的时候参数配置是不用的,servlet的是配置在web.xml中的,形式如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">


    <!-- 登陆验证码Kaptcha 2-->
    <servlet>
        <servlet-name>Kaptcha</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>
                边框颜色,合法值: r,g,b (and optional alpha) 或者
                white,black,blue.
            </description>
            <param-name>kaptcha.border.color</param-name>
            <param-value>black</param-value>
        </init-param>
        <init-param>
            <description>边框厚度,合法值:>0</description>
            <param-name>kaptcha.border.thickness</param-name>
            <param-value>1</param-value>
        </init-param>
        <init-param>
            <description>图片宽 200</description>
            <param-name>kaptcha.image.width</param-name>
            <param-value>200</param-value>
        </init-param>
        <init-param>
            <description>图片高 50</description>
            <param-name>kaptcha.image.height</param-name>
            <param-value>50</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>验证码长度 5</description>
            <param-name>kaptcha.textproducer.char.length</param-name>
            <param-value>5</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>字体大小 40px.</description>
            <param-name>kaptcha.textproducer.font.size</param-name>
            <param-value>40</param-value>
        </init-param>
        <init-param>
            <description>
                字体颜色,合法值: r,g,b 或者 white,black,blue.
            </description>
            <param-name>kaptcha.textproducer.font.color</param-name>
            <param-value>black</param-value>
        </init-param>
        <init-param>
            <description>文字间隔 2</description>
            <param-name>kaptcha.textproducer.char.space</param-name>
            <param-value>2</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>black</param-value>
        </init-param>
        <init-param>
            <description>
                图片样式: 水纹com.google.code.kaptcha.impl.WaterRipple
                鱼眼com.google.code.kaptcha.impl.FishEyeGimpy
                阴影com.google.code.kaptcha.impl.ShadowGimpy
            </description>
            <param-name>kaptcha.obscurificator.impl</param-name>
            <param-value>
                com.google.code.kaptcha.impl.WaterRipple
            </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>green</param-value>
        </init-param>
        <init-param>
            <description>背景颜色渐变,结束颜色</description>
            <param-name>kaptcha.background.clear.to</param-name>
            <param-value>white</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>
                session中存放验证码的key键
            </description>
            <param-name>kaptcha.session.key</param-name>
            <param-value>KAPTCHA_SESSION_KEY</param-value>
        </init-param>
        <init-param>
            <description>
                The date the kaptcha is generated is put into the
                HttpSession. This is the key value for that item in the
                session.
            </description>
            <param-name>kaptcha.session.date</param-name>
            <param-value>KAPTCHA_SESSION_DATE</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>Kaptcha</servlet-name>
        <url-pattern>/randomcode.jpg</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

2、使用springboot+springmvc框架

 这里新建一个maven项目,添加依赖

<dependency>  
    <groupId>com.github.penggle</groupId>  
    <artifactId>kaptcha</artifactId>  
    <version>2.3.2</version>  
</dependency>  

controller:

@Controller
public class UserController {
    @Autowired
    private Producer captchaProducer;

    @RequestMapping("/ran/random")
    public void checkCode(HttpServletRequest request,HttpServletResponse response) throws IOException{
        byte[] captchaChallengeAsJpeg = null;    
        ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();    
        try {    
            //生产验证码字符串并保存到session中  
            String createText = captchaProducer.createText();  
            request.getSession().setAttribute("checkCode", createText);  
            //使用生产的验证码字符串返回一个BufferedImage对象并转为byte写入到byte数组中  
            BufferedImage challenge = captchaProducer.createImage(createText);  
            ImageIO.write(challenge, "jpg", jpegOutputStream);  
        } catch (IllegalArgumentException e) {    
            response.sendError(response.SC_NOT_FOUND);    
            return;    
        }   

        //定义response输出类型为image/jpeg类型,使用response输出流输出图片的byte数组  
        captchaChallengeAsJpeg = jpegOutputStream.toByteArray();    
        response.setHeader("Cache-Control", "no-store");    
        response.setHeader("Pragma", "no-cache");    
        response.setDateHeader("Expires", 0);    
        response.setContentType("image/jpeg");    
        ServletOutputStream responseOutputStream =    
                response.getOutputStream();    
        responseOutputStream.write(captchaChallengeAsJpeg);    
        responseOutputStream.flush();    
        responseOutputStream.close();    
    }  
}

配置类CaptchaConfig:

这里@value是为了将相关属性写进application.properties,避免硬编码,为了方便测试我先注掉

package com.allan.base;

import java.util.Properties;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
/**
 * 验证码配置类
 * @author zhangzhuo
 *
 */
@Configuration
public class CaptchaConfig {
    @Value("${kaptcha.border}")
    private  String  border;
    @Value("${kaptcha.border.color}")
    private  String  borderColor;
    @Value("${kaptcha.textproducer.font.color}")
    private  String  fontColor;
    @Value("${kaptcha.image.width}")
    private  String  imageWidth;
    @Value("${kaptcha.image.height}")
    private  String  imageHeight;
    @Value("${kaptcha.session.key}")
    private  String  sessionKey;
    @Value("${kaptcha.textproducer.char.length}")
    private  String  charLength;
    @Value("${kaptcha.textproducer.font.names}")
    private  String  fontNames;

    @Bean(name="captchaProducer")
    public DefaultKaptcha getKaptchaBean(){
        DefaultKaptcha defaultKaptcha=new DefaultKaptcha();
        Properties properties=new Properties();
      /*  properties.setProperty("kaptcha.border", border);
        properties.setProperty("kaptcha.border.color", borderColor);
        properties.setProperty("kaptcha.textproducer.font.color", fontColor);
        properties.setProperty("kaptcha.image.width", imageWidth);
        properties.setProperty("kaptcha.image.height", imageHeight);
        properties.setProperty("kaptcha.session.key", sessionKey);
        properties.setProperty("kaptcha.textproducer.char.length", charLength);
        properties.setProperty("kaptcha.textproducer.font.names", fontNames);
        properties.setProperty("kaptcha.textproducer.font.size", "30"); */
        properties.setProperty("kaptcha.border", "yes");  
        properties.setProperty("kaptcha.border.color", "105,179,90");  
        properties.setProperty("kaptcha.textproducer.font.color", "blue");  
        properties.setProperty("kaptcha.image.width", "90");  
        properties.setProperty("kaptcha.image.height", "28");  
        properties.setProperty("kaptcha.textproducer.font.size", "28");  
        properties.setProperty("kaptcha.session.key", "code");  
        properties.setProperty("kaptcha.textproducer.char.length", "4");  
        properties.setProperty("kaptcha.textproducer.char.space", "2");
        properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.ShadowGimpy"); 
        properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
        properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");  
        Config config=new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
}

启动类:

package com.allan.server;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
@EnableAutoConfiguration
@ComponentScan(basePackages = {"com.allan.controller","com.allan.service","com.allan.base"})
@MapperScan(basePackages = "com.allan.mapper")
public class StartApp {
    public static void main(String[] args) {
        SpringApplication.run(StartApp.class, args);
    }
}

jsp:

<p class="main">
      <label>验证码: </label> <input name="randomCode"
               onkeyup="enterSubmit(event)" placeholder="验证码"
                style="width: 105px;" maxlength="4" /> <span class="yzm-pic">
      <img src="/ran/random" alt="验证码,点击图片更换"
                            onclick="this.src='/ran/random?random='+Math.random();" />
                        </span>
</p>

最终的效果图:

最后说下springboot除了上面写代码的形式还可以写成下面的配置文件:

可以定义applicationcontext-check.xml

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">  
    <bean id="captchaProducer" class="com.google.code.kaptcha.impl.DefaultKaptcha">  
        <property name="config">  
            <bean class="com.google.code.kaptcha.util.Config">  
                <constructor-arg type="java.util.Properties">  
                    <props>  
                        <prop key = "kaptcha.border ">yes</prop>  
                            <prop key="kaptcha.border.color">105,179,90</prop>  
                            <prop key="kaptcha.textproducer.font.color">blue</prop>  
                            <prop key="kaptcha.image.width">100</prop>  
                            <prop key="kaptcha.image.height">50</prop>  
                            <prop key="kaptcha.textproducer.font.size">27</prop>  
                            <prop key="kaptcha.session.key">code</prop>  
                            <prop key="kaptcha.textproducer.char.length">4</prop>  
                            <prop key="kaptcha.textproducer.font.names">宋体,楷体,微软雅黑</prop>  
                            <prop key="kaptcha.textproducer.char.string">0123456789ABCEFGHIJKLMNOPQRSTUVWXYZ</prop>  
                            <prop key="kaptcha.obscurificator.impl">com.google.code.kaptcha.impl.WaterRipple</prop>  
                            <prop key="kaptcha.noise.color">black</prop>  
                            <prop key="kaptcha.noise.impl">com.google.code.kaptcha.impl.DefaultNoise</prop>  
                            <prop key="kaptcha.background.clear.from">185,56,213</prop>  
                            <prop key="kaptcha.background.clear.to">white</prop>  
                            <prop key="kaptcha.textproducer.char.space">3</prop>  
                    </props>  
                </constructor-arg>  
            </bean>  
        </property>  
    </bean>  
  
</beans>  

如果写配置文件,这边在启动的时候需要引入配置文件

@ImportResource(locations={"classpath:applicationcontext-check.xml"})  

 

基本上就是这些,至于汉字组合或者计算形式的验证码,这边我们只要实现kaptcha.textproducer.impl和com.google.code.kaptcha.servlet.KaptchaServlet这两个类就行了,大家可以看下源码,很简单的。

最后的最后,如果有对于springboot不熟悉的同学可以看下我整理的这些文章,相信会有些帮助的http://www.cnblogs.com/allanzhang/category/1000281.html

jdk版源码:https://git.oschina.net/allanzhang/checkcode.git

posted @ 2017-05-29 09:46  小卖铺的老爷爷  阅读(1309)  评论(4编辑  收藏  举报


^
TOP