参考了他人的劳动成果,写了个粘连型验证码。代码如下:
package com.chenfei;
import java.io.*;
import java.net.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.swing.ImageIcon;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.awt.image.*;
import java.util.*;
import javax.imageio.*;
/** *//**
*
* @author http://www.tot.name
* @version
*/
public class Image extends HttpServlet{
private int frameColor = 0xFFFFFF;
/* 验证码前景色的各个分量范围, 大小为0-255 ,由小到大排列,包括R,G,B三个值 */
private int[] fgColorRange = new int[] { 0, 100 };
/* 对验证码各个字符进行变换时的缩放因子范围,包括x方向和y方向 */
private double[] scaleRange = new double[] { 0.6, 0.8};
/* 验证码各个字符进行变换时的旋转因子范围,单位为弧度 */
private double[] rotateRange = new double[]{-0.19823965696724605,0.19823965696724605};
// 验证码干扰线数
private int lineCount = 150;
private int[] disturbColor={180,255};
/**
* 根据一个范围生成一个随机颜色,其中颜色分量的最大值为255
*/
private Color getColor(int[] colorRange) {
int r = getRandomInRange(colorRange);
int g = getRandomInRange(colorRange);
int b = getRandomInRange(colorRange);
return new Color(r, g, b);
}
//生成整个图片背景颜色
public Color getbgColor(int fc, int bc) {
Random random = new Random();
if (fc > 255)
fc = 255;
if (bc > 255)
bc = 255;
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
/**
* 返回一个在参数指定范围内的随机数
*/
private int getRandomInRange(int[] range) {
if (range == null || range.length != 2) {
throw new RuntimeException("范围参数不合法,必须包含两个元素");
}
return (int) (Math.random() * (range[1] - range[0]) + range[0]);
}
/**
* 返回一个在参数指定范围内的随机数
*/
private float getRandomInRange(float[] range) {
if (range == null || range.length != 2) {
throw new RuntimeException("范围参数不合法,必须包含两个元素");
}
return (float) (Math.random() * (range[1] - range[0]) + range[0]);
}
/**
* 返回一个在参数指定范围内的随机数
*/
private double getRandomInRange(double[] range) {
if (range == null || range.length != 2) {
throw new RuntimeException("范围参数不合法,必须包含两个元素");
}
return Math.random() * (range[1] - range[0]) + range[0];
}
//产生四位随机码
private String generateCodeString(String dictionary) {
char[] dictionaryChars = dictionary.toCharArray();
int index1 = (int) (dictionaryChars.length * Math.random());
int index2 = (int) (dictionaryChars.length * Math.random());
int index3 = (int) (dictionaryChars.length * Math.random());
int index4 = (int) (dictionaryChars.length * Math.random());
char[] codeChars = new char[]{dictionaryChars[index1],dictionaryChars[index2],dictionaryChars[index3],dictionaryChars[index4]};
return String.valueOf(codeChars, 0, codeChars.length);
}
//生成验证码图片,每个字符都是一张独立的图片
private BufferedImage generateCheckCodeImage(String code)
{
char[] codeChars = code.toCharArray();//code就是随即产生的验证码
BufferedImage[] images = new BufferedImage[codeChars.length];
//设定字体
Font font;
font = new Font("Microsoft Sans Serif", Font.PLAIN,28);
/* 在生成验证码时,使用同一种颜色更不易被识别软件识别 */
// Color fgColor = getColor(fgColorRange);
int red[] = { 0, 255, 90, 255 }, green[] = { 0, 0, 90, 0 }, blue[] = { 0, 0, 90, 0 };
for (int i = 0; i < codeChars.length; i++) {
int height = 25;
/* 首先创建一个height * height 的图片 */
images[i] = new BufferedImage(height, height, BufferedImage.TYPE_INT_RGB);//
Graphics2D g2d = images[i].createGraphics();
/*-----使得绘制的图片背景透明-----*/
images[i] = g2d.getDeviceConfiguration().createCompatibleImage(height, height, Transparency.TRANSLUCENT);
g2d.dispose();
/*------------------------------*/
g2d = images[i].createGraphics();
g2d.setFont(font);
g2d.setColor(new Color(red[i], green[i], blue[i]));
/* 变换矩阵,对字符分别进行scale,rotate变换 */
AffineTransform affineTransform = new AffineTransform();
double scaleX=getRandomInRange(scaleRange);
double scaleY=getRandomInRange(scaleRange);
affineTransform.scale(scaleX, scaleY);
double rotateX=getRandomInRange(rotateRange);
affineTransform.rotate(rotateX, 15, 15);
g2d.setTransform(affineTransform);
FontRenderContext fontRenderContext = g2d.getFontRenderContext();
/* 计算绘制字符时的基线位置,使字符在图片中央处 */
int p_x = (int) ((height - g2d.getFontMetrics().charWidth(codeChars[i]))/2);
int p_y = (int) (height/2 + font.getLineMetrics(Character.toString(codeChars[i]),fontRenderContext).getAscent()/2);
g2d.drawString(Character.toString(codeChars[i]),p_x,p_y);
g2d.dispose();
}
return appendImages(images);//拼接验证码图片
}
//拼接验证码图片
private BufferedImage appendImages(BufferedImage[] images) {
int width=60;
int height = 25;
BufferedImage bgImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
Graphics2D g2d = bgImage.createGraphics();
g2d.setColor(getbgColor(200, 250));
g2d.fillRect(0, 0, width, height);
g2d.setColor(new Color(frameColor));
g2d.drawRect(0, 0, width, height);
Random random = new Random();
int red = 0, green = 0, blue = 0;
for (int i = 0; i < lineCount; i++) {
int xs = random.nextInt(width);
int ys = random.nextInt(height);
int xe = xs+random.nextInt(width/12);
int ye = ys+random.nextInt(height/8);
Color fgColor = getColor(disturbColor);
g2d.setColor(fgColor);
g2d.drawLine(xs, ys, xe, ye);
}
// 当前正在处理的图片
int index = 0;
// 绘制下一个图片时的位置
int drawX = 0;
if (images != null && images.length != 0) {
g2d.drawImage(images[index], drawX, 0, images[index].getWidth(), images[0].getHeight(), null);
drawX += images[index].getWidth();
index++;
}
while (index < images.length) {
int distance = calculateDistanceBetweenChar2(images[index - 1], images[index]);
g2d.drawImage(images[index], drawX - distance, 0, images[index].getWidth(), images[0].getHeight(), null);
drawX += images[index].getWidth() - distance;
index++;
}
g2d.dispose();
return bgImage;
}
private int min(int[] array) {
int result = Integer.MAX_VALUE;
for (int item : array) {
if (item < result) {
result = item;
}
}
return result;
}
/**
* 计算每个图片每行的左右空白像素个数. int[row][0]存储左边空白像素个数 int[row][1]存储右边空白像素个数
*/
private int[][] calculateBlankNum(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
int[][] result = new int[height][2];
for (int i = 0; i < result.length; i++) {
result[i][0] = 0;
result[i][1] = width;
}
int[] colorArray = new int[4];
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
colorArray = image.getRaster().getPixel(j, i, colorArray);
if (!checkArray(colorArray, new int[] { 0, 0, 0, 0 })) {
if (result[i][0] == 0) {
result[i][0] = j;
}
result[i][1] = width - j - 1;
}
}
}
return result;
}
/**
* 算法:按行扫描。计算相邻图片的距离
*/
private int calculateDistanceBetweenChar2(BufferedImage leftImage, BufferedImage rightImage) {
// 左图每行右侧空白字符个数列表
int[][] left = calculateBlankNum(leftImage);
// 右图每行左侧空白字符个数列表
int[][] right = calculateBlankNum(rightImage);
int[] tempArray = new int[leftImage.getHeight()];
for (int i = 0; i < left.length; i++) {
if (right[i][0] == 0) {
tempArray[i] = left[i][1] + leftImage.getWidth();
} else {
tempArray[i] = left[i][1] + right[i][0];
}
}
return min(tempArray);
}
/**
* 判断两个数组是否相等,要求长度相等,每个元素的值也相等
*/
private boolean checkArray(int[] arrayA, int[] arrayB) {
if (arrayA == null || arrayB == null) {
return false;
}
if (arrayA.length != arrayB.length) {
return false;
}
for (int i = 0; i < arrayA.length; i++) {
if (arrayA[i] != arrayB[i]) {
return false;
}
}
return true;
}
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
response.setContentType("image/jpeg");
response.setHeader("Pragma","No-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);
HttpSession session=request.getSession();
String dictionary="2345689ABCDEFGHJKMNPQRSUVXYWZ";
String sRand="";
sRand=generateCodeString(dictionary);
System.out.println(sRand);
BufferedImage image=generateCheckCodeImage(sRand);
// 将认证码存入SESSION
session.setAttribute("randCode",sRand);
ServletOutputStream responseOutputStream =response.getOutputStream();
// 输出图象到页面
ImageIO.write(image, "JPEG", responseOutputStream);
//以下关闭输入流!
responseOutputStream.flush();
responseOutputStream.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
}
web.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>image</servlet-name>
<servlet-class>com.chenfei.Image</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>image</servlet-name>
<url-pattern>/captcha</url-pattern>
</servlet-mapping>
</web-app>
部署, 运行:http://127.0.0.1:8080/工程名/captcha即可