Java Web:自定义验证码功能

    xiaoxiao2021-03-25  108

    本文内容大多基于官方文档和网上前辈经验总结,经过个人实践加以整理积累,仅供参考。


    1 生成随机字符串

    /** * 系统默认字符源:大小写字母和数字,去掉了[0,1,l,o,I,O]这几个容易混淆的字符 */ private static final String CAPTCHA_SOURCES = "23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"; /** * 使用系统默认字符源生成验证码 * @param size 验证码长度 * @return */ public static String generateCaptcha(int size) { return generateCaptcha(size, CAPTCHA_SOURCES); } /** * 使用指定字符源生成验证码 * @param size 验证码长度 * @param sources 验证码字符源 * @return */ public static String generateCaptcha(int size, String sources) { // 如果指定验证码字符源为空则使用使用系统默认字符源 if (sources == null || sources.length() == 0) { sources = CAPTCHA_SOURCES; } int sourcesLength = sources.length(); Random random = new Random(System.currentTimeMillis()); StringBuilder captcha = new StringBuilder(); for (int i = 0; i < size; i++) { captcha.append(sources.charAt(random.nextInt(sourcesLength - 1))); } return captcha.toString(); }

    单元测试:

    @Test public void test() { System.out.println(CaptchaUtil.generateCaptcha(4)); System.out.println(CaptchaUtil.generateCaptcha(6)); System.out.println(CaptchaUtil.generateCaptcha(4, "!@#$%&?")); }

    测试结果:

    PbFd yV5ycy &@!!

    2 输出特定字符串的图像文件

    /** * 指定验证码字体 */ private static final String FONT = "Calibri"; /** * 输出特定字符串的图像文件输出流 * @param width 验证码图片宽度 * @param height 验证码图片高度 * @param outputStream 验证码图片文件输出流 * @param captcha 验证码文本字符串 * @throws IOException */ public static void outputImage(int width, int height, OutputStream outputStream, String captcha) throws IOException { int size = captcha.length(); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Random random = new Random(); Graphics2D g2d = image.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Color[] colors = new Color[5]; Color[] colorSpaces = new Color[] { Color.WHITE, Color.CYAN, Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.YELLOW }; float[] fractions = new float[colors.length]; for (int i = 0; i < colors.length; i++) { colors[i] = colorSpaces[random.nextInt(colorSpaces.length)]; fractions[i] = random.nextFloat(); } Arrays.sort(fractions); g2d.setColor(getRandomColor(100, 160)); int fontSize = height - 8; Font font = new Font(FONT, Font.ITALIC, fontSize); g2d.setFont(font); char[] chars = captcha.toCharArray(); for (int i = 0; i < size; i++) { AffineTransform affine = new AffineTransform(); affine.setToRotation( Math.PI / 5 * random.nextDouble() * (random.nextBoolean() ? 1 : -1), (width / size) * i + fontSize / 2, height / 2); g2d.setTransform(affine); g2d.drawChars(chars, i, 1, ((width - 10) / size) * i + 5, height / 2 + fontSize / 2 - 6); } g2d.dispose(); ImageIO.write(image, "jpg", outputStream); } /** * 输出特定字符串的图像文件 * @param width 验证码图片宽度 * @param height 验证码图片高度 * @param outputFile 验证码图片文件 * @param captcha 验证码文本字符串 * @throws IOException */ public static void outputImage(int width, int height, File outputFile, String captcha) throws IOException { if (outputFile != null) { File dir = outputFile.getParentFile(); if (!dir.exists()) { dir.mkdirs(); } try { outputFile.createNewFile(); FileOutputStream fos = new FileOutputStream(outputFile); outputImage(width, height, fos, captcha); } catch (IOException e) { throw e; } } } private static Color getRandomColor(int fc, int bc) { if (fc > 255) { fc = 255; } if (bc > 255) { bc = 255; } Random random = new Random(); 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); }

    单元测试:

    @Test public void test() throws FileNotFoundException, IOException { CaptchaUtil.outputImage(400, 200, new FileOutputStream("D:/captcha.jpg"), "X4u9"); }

    测试生成的验证码图片:

    3 生成验证码文件并返回验证码文本字符串

    /** * 生成验证码文件并返回验证码文本字符串 * @param width 验证码图片宽度 * @param height 验证码图片高度 * @param outputFile 验证码图片文件 * @param size 验证码长度 * @return 验证码文本字符串 * @throws IOException */ public static String outputCaptchaImage(int width, int height, File outputFile, int size) throws IOException { String captcha = generateCaptcha(size); outputImage(width, height, outputFile, captcha); return captcha; } /** * 生成验证码文件输出流并返回验证码文本字符串 * @param width 验证码图片宽度 * @param height 验证码图片高度 * @param outputStream 验证码图片文件 * @param size 验证码长度 * @return 验证码文本字符串 * @throws IOException */ public static String outputCaptchaImage(int width, int height, OutputStream outputStream, int size) throws IOException { String captcha = generateCaptcha(size); outputImage(width, height, outputStream, captcha); return captcha; }

    单元测试:

    @Test public void test() throws FileNotFoundException, IOException { CaptchaUtil.outputCaptchaImage(400, 200, new File("D:/captcha.jpg"), 5); }

    测试生成的验证码图片:

    4 增强验证码图片识别难度

    重写以下方法

    public static void outputImage(int width, int height, OutputStream outputStream, String captcha) throws IOException { int size = captcha.length(); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Random random = new Random(); Graphics2D g2d = image.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Color[] colors = new Color[5]; Color[] colorSpaces = new Color[] { Color.WHITE, Color.CYAN, Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.YELLOW }; float[] fractions = new float[colors.length]; for (int i = 0; i < colors.length; i++) { colors[i] = colorSpaces[random.nextInt(colorSpaces.length)]; fractions[i] = random.nextFloat(); } Arrays.sort(fractions); // 设置边框颜色 g2d.setColor(Color.GRAY); g2d.fillRect(0, 0, width, height); // 设置背景色 Color color = getRandomColor(200, 250); g2d.setColor(color); g2d.fillRect(0, 2, width, height - 4); // 绘制干扰线 random = new Random(); // 设置线条颜色 g2d.setColor(getRandomColor(160, 200)); for (int i = 0; i < 20; i++) { int x = random.nextInt(width - 1); int y = random.nextInt(height - 1); int x1 = random.nextInt(6) + 1; int y1 = random.nextInt(12) + 1; g2d.drawLine(x, y, x + x1 + 40, y + y1 + 20); } // 添加噪点 // 噪声率 float noiseRatio = 0.05f; int area = (int) (noiseRatio * width * height); for (int i = 0; i < area; i++) { int x = random.nextInt(width); int y = random.nextInt(height); int rgb = getRandomIntColor(); image.setRGB(x, y, rgb); } // 扭曲图片 shear(g2d, width, height, color); g2d.setColor(getRandomColor(100, 160)); int fontSize = height - 8; Font font = new Font(FONT, Font.ITALIC, fontSize); g2d.setFont(font); char[] chars = captcha.toCharArray(); for (int i = 0; i < size; i++) { AffineTransform affine = new AffineTransform(); affine.setToRotation( Math.PI / 5 * random.nextDouble() * (random.nextBoolean() ? 1 : -1), (width / size) * i + fontSize / 2, height / 2); g2d.setTransform(affine); g2d.drawChars(chars, i, 1, ((width - 10) / size) * i + 5, height / 2 + fontSize / 2 - 6); } g2d.dispose(); ImageIO.write(image, "jpg", outputStream); } private static int getRandomIntColor() { int[] rgb = getRandomRgb(); int color = 0; for (int c : rgb) { color = color << 8; color = color | c; } return color; } private static int[] getRandomRgb() { int[] rgb = new int[3]; for (int i = 0; i < 3; i++) { rgb[i] = new Random().nextInt(255); } return rgb; } private static void shear(Graphics g, int width, int height, Color color) { shearX(g, width, height, color); shearY(g, width, height, color); } private static void shearX(Graphics g, int width, int height, Color color) { Random random = new Random(); int period = random.nextInt(2); boolean borderGap = true; int frames = 1; int phase = random.nextInt(2); for (int i = 0; i < height; i++) { double d = (double) (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames); g.copyArea(0, i, width, 1, (int) d, 0); if (borderGap) { g.setColor(color); g.drawLine((int) d, i, 0, i); g.drawLine((int) d + width, i, width, i); } } } private static void shearY(Graphics g, int width, int height, Color color) { Random random = new Random(); int period = random.nextInt(40) + 10; boolean borderGap = true; int frames = 20; int phase = 7; for (int i = 0; i < width; i++) { double d = (double) (period >> 1) * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames); g.copyArea(i, 0, 1, height, 0, (int) d); if (borderGap) { g.setColor(color); g.drawLine(i, (int) d, i, 0); g.drawLine(i, (int) d + height, i, height); } } }

    单元测试生成的验证码图片:

    5 编写生成验证码图片的Servlet

    import java.io.IOException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; public class CaptchaGenerator extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { doPost(request, response); } @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setHeader("Pragma", "No-cache"); response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expires", 0); response.setContentType("image/jpeg"); // 生成随机验证码字符串 String captcha = CaptchaUtil.generateCaptcha(5); // 存入Session HttpSession session = request.getSession(true); session.setAttribute("captcha", captcha.toLowerCase()); // 生成图片 CaptchaUtil.outputImage(400, 200, response.getOutputStream(), captcha); } }

    6 配置生成验证码图片的Servlet

    <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>Captcha</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>captchaGenerator</servlet-name> <servlet-class>CaptchaGenerator</servlet-class> </servlet> <servlet-mapping> <servlet-name>captchaGenerator</servlet-name> <url-pattern>/refresh_captcha.ajax</url-pattern> </servlet-mapping> </web-app>

    7 编写 JSP 页面

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=Edge"> <title>Hello Captcha</title> </head> <body> The is Captcha Image: <br> <img src="refresh_captcha.ajax"> </body> </html>

    运行后页面:

    转载请注明原文地址: https://ju.6miu.com/read-24432.html

    最新回复(0)