package com.toy; import java.util.Random; import java.io.UnsupportedEncodingException; import java.io.OutputStream; import java.awt.image.BufferedImage; import javax.imageio.ImageIO; import java.awt.*; import java.io.*; /** * 一个能生成验证码的类 * 默认四个汉字 * 使用该类生成验证码时 * @author jiangjizhong */ public class VerifyCode { private final Random random = new Random(System.nanoTime()); private int width; private int height; private int charNumber = 4; private float yawpRate = 0.2f; //噪声率 private OutputStream outputStream; private char[] code; public VerifyCode() { //empty } /** * 返回生成的验证码字符串 * @return */ public String getVerifyCode(){ return new String(code); } public VerifyCode(int width, int height) { this.width = width; this.height = height; } private void drawPane(Graphics2D g2d){ g2d.setColor(Color.BLACK); g2d.setStroke(new BasicStroke(2.0f)); g2d.drawRect(0, 0, width, height); } private void drawChars(Graphics2D g2d){ float scale = 1f; int size = (int) (scale * height); int offsetHeight = (height + size) / 2; StringBuffer sb = new StringBuffer(1); sb.setLength(1); g2d.setColor(Color.BLACK); code = new char[charNumber]; for (int i = 0; i < charNumber; i++) { //计算第i个字的x偏移 int offsetWidth = i * size; //随机大小 int charSize = this.getRandomIntBetween((int) (size * 0.5), size); //随机样式 int fontStyle = this.getRandomBoolean() ? Font.ITALIC : Font.ITALIC; g2d.setFont(new Font("隶书", fontStyle, charSize)); //随机颜色 //g2d.setColor(this.getRandomColor()); //随机上移一点 int moveUp = random.nextInt((int) (0.3 * height)); //随机字符 char c = this.getRandomChar(); code[i] = c; sb.setCharAt(0, c); g2d.drawString(sb.toString(), offsetWidth, offsetHeight - moveUp); } } private void drawSmallChars(Graphics2D g2d){ final int lines = 5; int fontSize = this.getHeight() / lines; int number = this.getWidth() / fontSize; g2d.setFont(new Font("隶书", Font.PLAIN, fontSize)); g2d.setColor(this.getRandomColor()); for(int i=0; i<10;i++){ StringBuffer sb = new StringBuffer(number); for(int j=0; j<number; j++){ sb.append(this.getRandomChar()); } g2d.drawString(sb.toString(), 0, (i + 1) * fontSize); } } /** * 主要方法,开始生成 */ public void write() { BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = bi.createGraphics(); g2d.setColor(Color.WHITE); g2d.fillRect(0, 0, width, height); //画方框9/10期望宽度 this.drawPane(g2d); //设定字体,字体大小为4/5方框,居中 this.drawChars(g2d); /*补充注释:有些人添加干扰时,喜欢使用随机线条。我认为这个方法一点都不好,不仅是图片极度丑陋,而且根本起不到干扰的作用。*/ //背景上随机10行小字 this.drawSmallChars(g2d); //添加噪点 int area = (int) (this.getYawpRate() * height * width); for (int i = 0; i < area; i++) { int x = random.nextInt(width); int y = random.nextInt(height); int rgb = this.getRandomIntColor(); bi.setRGB(x, y, rgb); } try { ImageIO.write(bi, "jpeg", outputStream); } catch (Exception exc) { exc.printStackTrace(); } } private boolean getRandomBoolean() { int randInt = random.nextInt(10); if (randInt > 4) { return true; } else { return false; } } private Color getRandomColor() { int[] rgb = this.getRandomRgb(); return new Color(rgb[0], rgb[1], rgb[2]); } private int getRandomIntColor() { int[] rgb = this.getRandomRgb(); int color = 0; for (int c : rgb) { color = color << 8; color = color | c; } return color; } private int[] getRandomRgb() { int[] rgb = new int[3]; for (int i = 0; i < 3; i++) { rgb[i] = random.nextInt(255); } return rgb; } /** * 返回一个随机汉字字符。 * 采用随机生成两个byte按gb2312编码构成一个String的算法。 * 两个字节在B0A1到D7F9之间,是常用简体字 * 所以,第一个字节在B0(十进制176)到D7(十进制215)之间,第二个字节在A1(十进制161)到F9(十进制249)之间 * @return */ protected char getRandomChar() { final int minFirstByte = 176; final int maxFirstByte = 215; final int minSecondByte = 161; final int maxSecondByte = 249; byte[] b = new byte[2]; b[0] = (byte) (getRandomIntBetween(minFirstByte, maxFirstByte)); b[1] = (byte) (getRandomIntBetween(minSecondByte, maxSecondByte)); try { String s = new String(b, "gb2312"); assert s.length() == 1; return s.charAt(0); } catch (UnsupportedEncodingException uee) { //重试 return getRandomChar(); } } /** * 获取两个整数之间的某个随机数,包含较小的整数,不包含较大的整数 * @param first * @param second * @return 随机数 */ private int getRandomIntBetween(int first, int second) { if (second < first) { int tmp = first; first = second; second = tmp; } return random.nextInt(second - first) + first; } public int getWidth() { return width; } /** * 验证码图片的宽度,一般的,宽度应该是高度乘以字符数目 */ public void setWidth(int width) { this.width = width; } public int getHeight() { return height; } /** * 验证码图片的高度 */ public void setHeight(int height) { this.height = height; } public OutputStream getOutputStream() { return outputStream; } /** * 验证码输出地址,如果是在jsp或servlet里使用,这里的outputStream就是从Response对象里获取的outputStream */ public void setOutputStream(OutputStream outputStream) { this.outputStream = outputStream; } public int getCharNumber() { return charNumber; } /** * 生成验证码的字符数量,默认四个 */ public void setCharNumber(int charNumber) { this.charNumber = charNumber; } public float getYawpRate() { return yawpRate; } /** * 设定噪声率,既图像上出现的噪声像素占全部像素的比例 * @param yawpRate */ public void setYawpRate(float yawpRate) { this.yawpRate = yawpRate; } }
使用该验证码的jsp文件:
<%@page contentType="image/jpeg" pageEncoding="UTF-8" import="com.toy.*;"%> <% out.clear(); response.reset(); //设定相应头 //不缓存 response.setHeader("Pragma","No-cache"); response.setHeader("Cache-Control","no-cache"); response.setDateHeader("Expires", 0); int height = 50;//验证码图片高度 int width = 200;//验证码图片宽度 int charNumber = 4; if(request.getParameter("height") != null){ height = Integer.parseInt(request.getParameter("height")); } if(request.getParameter("width") != null){ width = Integer.parseInt(request.getParameter("width")); } if(request.getParameter("charnum") != null){ charNumber = Integer.parseInt(request.getParameter("charNumber")); } VerifyCode vc = new VerifyCode(width, height); vc.setCharNumber(charNumber); vc.setOutputStream(response.getOutputStream()); vc.write(); session.setAttribute("VerifyCode", vc.getVerifyCode()); %>