package com.fullgoal.ip.config; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Image; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Random; import javax.imageio.ImageIO; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /* 验证码图片生成步骤 创建BufferedImage对象。 获取BufferedImage的画笔,即调用getGraphics()方法获取Graphics对象。 调用Graphics对象的setColor()方法和fillRect()方法设置图片背景颜色。 调用Graphics对象的setColor()方法和drawLine()方法设置图片干扰线。 调用BufferedImaged对象的setRGB()方法设置图片的噪点。 调用Graphics对象的setColor()方法、setFont()方法和drawString()方法设置图片验证码。 因为验证码的图片的宽度和高度要根据网站的风格来确定的,所以字体的大小需要根据图片的宽度和高度来确定,用到了小小的技巧。 */ public class Verification { private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"; /** * 生成一个宽为width, 高为height, 验证码为vcode的图片 * @param width 图片的宽 * @param height 图片的高 * @param vcode 验证码字符串 * @return 返回图片验证码 */ public static BufferedImage getImage(int width, int height, String vvcode){ return getImage(width, height, vvcode, 20); } /** * 生成一个宽为width, 高为height, 验证码为vvcode的图片,图片中干扰线的条数为lineCnt * @param width 图片的宽 * @param height 图片的高 * @param vvcode 验证码字符串 * @param lineCnt 干扰线的条数,建议为10条左右,可根据结果适当调整 * @return 返回图片验证码 */ public static BufferedImage getImage(int width, int height, String vvcode, int lineCnt){ return createImage(width, height, vvcode, lineCnt, 0.01); } /** * 生成一个宽为width, 高为height, 验证码为vvcode的图片,图片中干扰线的条数为lineCnt * 噪声比为noiseRate,即图片中噪音像素点的百分比 * @param width 图片的宽 * @param height 图片的高 * @param vvcode 验证码字符串 * @param lineCnt 干扰线的条数,建议为10条左右,可根据结果适当调整 * @param noiseRate 图片中噪音像素点占总像素的百分比 * @return 返回图片验证码 */ public static BufferedImage getImage(int width, int height, String vvcode, int lineCnt, double noiseRate){ return createImage(width, height, vvcode, lineCnt, noiseRate); } /** * * 生成一个宽为width, 高为height, 验证码为vvcode的图片,图片中干扰线的条数为lineCnt * 噪声比为noiseRate,即图片中噪音像素点的百分比 * @param width 图片的宽 * @param height 图片的高 * @param vvcode 验证码字符串 * @param lineCnt 干扰线的条数,建议为10条左右,可根据结果适当调整 * @param noiseRate 图片中噪音像素点占总像素的百分比 * @return 返回图片验证码 */ private static BufferedImage createImage(int width, int height, String vvcode, int lineCnt, double noiseRate){ int fontWidth = ((int)(width * 0.8)) / vvcode.length(); int fontHeight = (int)(height * 0.7); //为了在任意的width和height下都能生成良好的验证码, //字体的大小为fontWdith何fontHeight中的小者, int fontSize = Math.min(fontWidth, fontHeight); //drawString时要用到 int paddingX = (int) (width * 0.1); int paddingY = height - (height - fontSize) / 2; //创建图像 BufferedImage buffimg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); //获得画笔 Graphics g = buffimg.getGraphics(); //设置画笔的颜色 g.setColor(getRandColor(200, 255)); //然后填充一个矩形,即设置背景色 g.fillRect(0, 0, width, height); // 设置干扰线 for (int i = 0; i < lineCnt; i++) { //随机获取干扰线的起点和终点 int xs = (int)(Math.random() * width); int ys = (int)(Math.random() * height); int xe = (int)(Math.random() * width); int ye = (int)(Math.random() * height); g.setColor(getRandColor(1, 255)); g.drawLine(xs, ys, xe, ye); } // 添加噪点 int area = (int) (noiseRate * width * height); for(int i=0; i<area; ++i){ int x = (int)(Math.random() * width); int y = (int)(Math.random() * height); buffimg.setRGB(x, y, (int)(Math.random() * 255)); } //设置字体 Font font = new Font("Ravie", Font.PLAIN, fontSize); g.setFont(font); for(int i=0; i<vvcode.length(); ++i){ String ch = vvcode.substring(i, i+1); g.setColor(getRandColor(1, 199)); g.drawString(ch, paddingX + fontWidth * i, paddingY); } return buffimg; } /** * 获取随机的颜色,r,g,b的取值在L到R之间 * @param L 左区间 * @param R 右区间 * @return 返回随机颜色值 */ private static Color getRandColor(int L, int R){ if(L > 255) L = 255; if(R > 255) R = 255; if(L < 0) L = 0; if(R < 0) R = 0; int interval = R - L; int r = L + (int)(Math.random() * interval); int g = L + (int)(Math.random() * interval); int b = L + (int)(Math.random() * interval); return new Color(r, g, b); } /** * 随机生成若干个由大小写字母和数字组成的字符串 * @param len 随机生成len个字符 * @return 返回随机生成的若干个由大小写字母和数字组成的字符串 */ public static String getRandvvcode(int len){ String vvcode = ""; for(int i=0; i<len; ++i){ int index = (int)(Math.random() * ALPHABET.length()); vvcode = vvcode + ALPHABET.charAt(index); } return vvcode; } /** * 将图片转为byte数组 * @param image 图片 * @return 返回byte数组 * @throws IOException */ public static byte[] getByteArray(BufferedImage image) throws IOException{ ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(image, "png", baos); return baos.toByteArray(); //ByteArrayOutputStream 不需要close } //使用验证码图片 //在verificationvvcode.java这个servlet中调用上面的类生成验证码图片,然后将图片返回给客户端。 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); //随机生成字符串,并写入session String vcode = Verification.getRandvvcode(4); session.setAttribute("verification", vcode); BufferedImage image = com.fullgoal.ip.config.Verification.getImage(100,30, vcode, 5); response.setContentType("image/png"); OutputStream out = response.getOutputStream(); //com.fullgoal.ip.config.Verification为包名 out.write(com.fullgoal.ip.config.Verification.getByteArray(image)); out.flush(); out.close(); } }