我写的验证码生成方案,可防止绝大多数机械识别。
程序员文章站
2023-12-30 12:15:58
...
web.xml
servlet类文件
在我的机器上,这个程序每秒钟可以产生大约83张验证码。验证码图样如下
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <!-- ******************************************** --> <!-- **********验证码生成器************************** --> <!-- ******************************************** --> <servlet> <servlet-name>CaptchaServlet</servlet-name> <servlet-class>com.lowca.activity.web.servlet.CaptchaServlet</servlet-class> <init-param> <param-name>width</param-name> <param-value>150</param-value> </init-param> <init-param> <param-name>height</param-name> <param-value>100</param-value> </init-param> <init-param> <param-name>bgColor</param-name> <param-value>255,255,255</param-value> </init-param> <init-param> <param-name>fontColor</param-name> <param-value>0,255,0</param-value> </init-param> <init-param> <param-name>fontSize</param-name> <param-value>44</param-value> </init-param> <init-param> <param-name>fontCount</param-name> <param-value>4</param-value> </init-param> <init-param> <param-name>fontName</param-name> <param-value>宋体</param-value> </init-param> <init-param> <param-name>chars</param-name> <!-- 去掉了容易造成误解的字符,例如:i o z 和 1 0 2 --> <param-value>ABCDEFGHJKLMNPQRSTUVWXYabcdefghjkmnpqrstuvwxy3456789</param-value> </init-param> <init-param> <param-name>sessionKey</param-name> <param-value>com.lowca.activity.captcha</param-value> </init-param> <init-param> <param-name>maxRotateAngle</param-name> <param-value>40</param-value> </init-param> <init-param> <param-name>fontPadding</param-name> <param-value>-15</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>CaptchaServlet</servlet-name> <url-pattern>/captcha.jpg</url-pattern> </servlet-mapping> </web-app>
servlet类文件
package com.lowca.activity.web.servlet; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.IOException; import javax.imageio.ImageIO; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; public class CaptchaServlet extends HttpServlet { private static final long serialVersionUID = 1L; private static final String CONTENT_TYPE = "image/jpeg"; // ==================以下变量用于保存配置参数的默认值 ================== /** * 验证码的宽度 */ private int width = 200; /** * 验证码的高度 */ private int height = 150; /** * 验证码背景色 */ private Color bgColor = Color.WHITE; /** * 文字颜色 */ private Color fontColor = Color.GREEN; /** * 验证码字符字号 */ private int fontSize = 42; /** * 验证码字符个数 */ private int fontCount = 4; /** * 验证码字符间距,单位是像素 */ private int fontPadding = -15; /** * 验证码字符最大旋转角度 */ private int maxRotateAngle = 40; /** * 验证码字体 */ private String fontName = "宋体"; /** * 验证码用到的字符 */ private String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; /** * 验证码在session中的键名 */ private String sessionKey = "com.lowca.activity.captcha"; // ==================以下变量用于在初始化时候保存数据,减少重复计算 ================== private Font font; private char[] seedArray; private int offsetX = 0; private int offsetY = 0; /** * 生成由数字和字母随机组成的图片 */ protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, java.io.IOException { BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = buffImg.createGraphics(); g2d.setColor(bgColor); g2d.fillRect(0, 0, width, height); g2d.setPaint(fontColor); g2d.setFont(font); // 旋转文本 StringBuffer randCode = new StringBuffer(); for (int i = 0; i < fontCount; i++) { g2d.setStroke(new BasicStroke((float) (Math.random() * 5))); double angle = Math.random() * maxRotateAngle * Math.PI / 180; int p = (int) (Math.random() * chars.length()); String word = String.valueOf(seedArray[p]); randCode.append(word); int x = offsetX + i * (fontSize + fontPadding); AffineTransform origXform = g2d.getTransform(); AffineTransform newXform = (AffineTransform) (origXform.clone()); newXform.rotate(angle, x, offsetY); g2d.setTransform(newXform); g2d.drawString(word, x, offsetY); g2d.setTransform(origXform); } g2d.setStroke(new BasicStroke(2.0f)); int x = offsetX - (int) (20 * Math.random()); int y = offsetY - (int) (20 * Math.random()); int w = (int) (width - 10 - Math.random() * 6); int h = (int) (height - 10 - Math.random() * 6); int startAngle = (int) (Math.random() * 16 + 45); int arcAngle = (int) (Math.random() * 45 + 90); g2d.drawArc(x, y, w, h, startAngle, arcAngle); g2d.dispose(); // 将四位数字的验证码保存到Session里面 HttpSession session = req.getSession(); session.setAttribute(sessionKey, randCode); // 禁止图像缓存 resp.setHeader("Prama", "no-cache"); resp.setHeader("Coche-Control", "no-cache"); resp.setDateHeader("Expires", 0); // 将图像输出到Servelt输出流中 ServletOutputStream out = resp.getOutputStream(); ImageIO.write(buffImg, "jpeg", out); out.close(); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType(CONTENT_TYPE); } public void init() throws ServletException { // 初始化配置参数 String param = getInitParameter("width"); if (NumberUtils.isDigits(param)) { width = Integer.parseInt(param); } param = getInitParameter("height"); if (NumberUtils.isDigits(param)) { height = Integer.parseInt(param); } param = getInitParameter("bgColor"); if (param != null && param.matches("^\\d+,\\d+,\\d+$")) { bgColor = getColor(param); } param = getInitParameter("fontColor"); if (param != null && param.matches("^\\d+,\\d+,\\d+$")) { fontColor = getColor(param); } param = getInitParameter("fontSize"); if (NumberUtils.isDigits(param)) { fontSize = Integer.parseInt(param); } param = getInitParameter("maxRotateAngle"); if (NumberUtils.isDigits(param)) { maxRotateAngle = Integer.parseInt(param); } param = getInitParameter("fontPadding"); if (param != null && param.matches("-?\\d+")) { fontPadding = Integer.parseInt(param); } param = getInitParameter("fontName"); if (StringUtils.isNotBlank(param)) { fontName = param; } param = getInitParameter("chars"); if (StringUtils.isNotBlank(param)) { chars = param; } param = getInitParameter("sessionKey"); if (StringUtils.isNotBlank(sessionKey)) { sessionKey = param; } // 初始化状态变量 font = new Font(fontName, Font.ITALIC, fontSize); seedArray = chars.toCharArray(); offsetX = width / 2 - fontCount * fontSize / 2 - (fontCount - 1) * fontPadding / 2 - 8; offsetY = height / 2 + fontSize / 2 - 8; } private Color getColor(String rgb) { String[] array = rgb.split(","); return new Color(Integer.parseInt(array[0]), Integer.parseInt(array[1]), Integer.parseInt(array[2])); } }
在我的机器上,这个程序每秒钟可以产生大约83张验证码。验证码图样如下