当前位置:首页 > 技术 > 正文内容

Java Web 开发中的图形验证码实现方案

访客 技术 2026年8月30日 1

在构建 Web 应用时,为了防止脚本程序恶意刷单、自动注册或暴力破解账号,通常需要在关键交互环节加入图形验证码。这种机制通过要求用户识别特定的字符或图像来区分人类与机器。当然,考虑到用户体验,验证码的复杂度需要适度平衡。本文将演示如何在传统的 Java Web 体系中,利用 Servlet 生成动态图片并结合 JSP 前端完成验证流程。

1. 页面交互设计 (front-end)

前端主要承担展示图片和接收用户输入的任务。为了提升体验,当用户无法看清当前图片时,应提供一键刷新功能。以下是一个简化后的登录表单页面,使用了标准的 HTML 结构和内联脚本处理图片更新请求。

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>安全验证页</title>
    <style>
        body { font-family: Arial, sans-serif; padding: 20px; }
        .form-group { margin-bottom: 10px; }
    </style>
    <script type="text/javascript">
        function refreshVerificationImage() {
            const imgElement = document.getElementById("verifyPic");
            const timeStmp = new Date().getTime();
            // 添加时间戳强制浏览器忽略缓存
            imgElement.src = "${pageContext.request.contextPath}/api/captcha/gen?t=" + timeStmp;
            document.getElementById("inputCode").value = "";
            document.getElementById("inputCode").focus();
        }
    </script>
</head>
<body>
    <center>
        <h2>身份验证</h2>
        <form action="${pageContext.request.contextPath}/api/captcha/check" method="post">
            <div class="form-group">
                请输入验证码:<input type="text" id="inputCode" name="userInput"/>
            </div>
            <div class="form-group">
                <img id="verifyPic" src="${pageContext.request.contextPath}/api/captcha/gen" onclick="refreshVerificationImage()" />
                <a href="javascript:void(0)" onclick="refreshVerificationImage()">点击换一张</a>
            </div>
            <div>
                <button type="submit">提交验证</button>
            </div>
        </form>
    </center>
</body>
</html>

2. 后端图片生成逻辑 (backend-generator)

为了增加识别难度,生成的验证码不应是简单的文字。我们需要使用 Java 的 AWT 库创建 BufferedImage 对象,绘制背景、干扰线以及随机字符。生成的字符串需要暂存到用户的 Session 中,以便后续比对。

import java.io.IOException;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.imageio.ImageIO;
import java.util.Random;

@WebServlet("/api/captcha/gen")
public class CaptchaGenerator extends HttpServlet {
    private static final int WIDTH = 120;
    private static final int HEIGHT = 40;
    private static final String CHAR_POOL = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置响应类型,告知浏览器这是图片资源
        resp.setContentType("image/jpeg");
        // 禁止浏览器缓存验证码图片
        resp.setHeader("Pragma", "no-cache");
        
        BufferedImage bufferImg = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics g = bufferImg.getGraphics();
        
        // 1. 绘制随机颜色背景
        Random rand = new Random();
        g.setColor(new Color(rand.nextInt(200) + 50, rand.nextInt(200) + 50, rand.nextInt(200) + 50));
        g.fillRect(0, 0, WIDTH, HEIGHT);
        
        // 2. 绘制干扰线条 (可选)
        for (int i = 0; i < 10; i++) {
            g.setColor(new Color(rand.nextInt(255), rand.nextInt(255), rand.nextInt(255)));
            int x1 = rand.nextInt(WIDTH);
            int y1 = rand.nextInt(HEIGHT);
            int x2 = rand.nextInt(WIDTH);
            int y2 = rand.nextInt(HEIGHT);
            g.drawLine(x1, y1, x2, y2);
        }
        
        // 3. 绘制随机字符
        StringBuilder correctCode = new StringBuilder();
        g.setFont(new Font("Times New Roman", Font.BOLD, 24));
        
        for (int i = 0; i < 4; i++) {
            g.setColor(new Color(rand.nextInt(100), rand.nextInt(200), rand.nextInt(200)));
            String charStr = String.valueOf(CHAR_POOL.charAt(rand.nextInt(CHAR_POOL.length())));
            g.drawString(charStr, 20 + i * 25, 28);
            correctCode.append(charStr);
        }
        
        // 释放绘图资源
        g.dispose();
        
        // 将正确码存入 Session,注意大小写敏感配置
        req.getSession().setAttribute("session_captcha", correctCode.toString().toLowerCase());
        
        // 输出流写入图片数据
        ImageIO.write(bufferImg, "jpg", resp.getOutputStream());
    }
}

3. 后端验证逻辑 (backend-validator)

当用户提交表单后,系统需要将用户输入的字符串与 Session 中存储的原始验证码进行对比。为了提高安全性,通常建议验证失败后立即销毁该次验证码,防止重放攻击。

@WebServlet("/api/captcha/check")
public class CaptchaChecker extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        res.setCharacterEncoding("UTF-8");
        res.setContentType("text/html;charset=UTF-8");
        
        String inputVal = req.getParameter("userInput");
        String storedVal = (String) req.getSession().getAttribute("session_captcha");
        
        PrintWriter out = res.getWriter();
        
        if (storedVal != null && storedVal.equalsIgnoreCase(inputVal)) {
            // 验证成功,可跳转至登录页面或返回 JSON 成功标志
            // 成功后可选择清除 session 中的验证码
            req.getSession().removeAttribute("session_captcha"); 
            out.println("<h3><font color='green'>验证通过,系统将继续处理请求</font></h3>");
            // out.println(jsonResponse(true)); 
        } else {
            // 验证失败,提示错误
            out.println("<h3><font color='red'>验证码输入错误,请重新尝试</font></h3>");
            // 强制刷新验证码链接引导
            out.println("<a href='/api/captcha/gen'>生成新验证码</a>");
        }
    }
}

上述代码完成了从图片绘制到结果比对的完整闭环。在实际生产环境中,还可以引入数学算术题、滑块拼图等更复杂的算法,或者使用第三方云服务来进一步降低被 OCR 技术攻破的风险。

返回列表

上一篇:鸿蒙应用视频播放异常排查与实战指南

没有最新的文章了...

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。