Servlet&&粘连型验证码

参考了他人的劳动成果,写了个粘连型验证码。代码如下:

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即可

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值