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

1、简单介绍

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

项目效果图:

JAVA实用案例之验证码开发-LMLPHP

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

2、jdk原生生成验证码

效果:

JAVA实用案例之验证码开发-LMLPHP

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组件生成验证码

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

数字字母组合

JAVA实用案例之验证码开发-LMLPHP

数字字母汉字组合

JAVA实用案例之验证码开发-LMLPHP

算数计算

JAVA实用案例之验证码开发-LMLPHP

3.1 kaptcha的参数详解

Constant描述默认值
kaptcha.border图片边框,合法值:yes , noyes
kaptcha.border.color边框颜色,合法值: r,g,b (and optional alpha) 或者 white,black,blue.black
kaptcha.border.thickness边框厚度,合法值:>01
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.keysession keyKAPTCHA_SESSION_KEY
kaptcha.session.datesession dateKAPTCHA_SESSION_DATE

3.2 代码讲解

1、servlet方式

JAVA实用案例之验证码开发-LMLPHP

上图可以看到,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>

最终的效果图:

JAVA实用案例之验证码开发-LMLPHP

最后说下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

05-07 15:43