参考了他人的劳动成果,写了个粘连型验证码。代码如下:
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即可