当前位置:

首页 > 编程开发 > 使用JSP+JavaScript实现验证码登录功能

使用JSP+JavaScript实现验证码登录功能

利用JSP、Servlet和JavaScript技术实现验证码登录功能,涵盖动态验证码图片生成、前端展示与刷新、表单提交及后台校验。技术要点包括防止缓存、设置过期时间、一次消费机制及大小写忽略处理,以增强系统安全性与用户体验。

引言

在现代 Web 应用中,用户身份验证是安全体系的第一道防线。然而,单纯依赖用户名和密码的登录机制早已不堪一击——自动化脚本、暴力破解、爬虫攻击层出不穷。一个看似简单的“验证码”功能,实则承载着抵御机器流量的重要职责。

最近重构一个传统 Ja va Web 项目时,重新审视了这一经典安全组件的设计与实现。今天就来分享如何使用 JSP + Servlet + Ja vaScript 技术栈,从零搭建一个结构清晰、交互流畅、具备基础防攻击能力的验证码登录系统。

整个过程并不复杂,但涉及前后端协作、会话管理、图像生成等多个关键点。我们将一步步完成:动态验证码图片生成 → 前端展示与刷新 → 表单提交 → 后台校验全流程,并在最后探讨其可扩展性与未来演进方向。

动态验证码是如何工作的?

验证码的核心逻辑其实很朴素:服务器生成一段随机字符,绘制成干扰性强的图片供人识别,同时将原始值存储在当前用户的 Session 中;当用户输入后,后台比对输入值与 Session 中的记录是否一致。

这个过程中有几个关键设计必须注意:

  • 防止缓存:浏览器不能缓存验证码图片,否则点击刷新无效
  • 时效控制:验证码不应永久有效,通常设置为60秒过期
  • 一次消费:成功验证后应立即清除,防止重放攻击
  • 大小写处理:建议统一转小写或大写进行比较,提升用户体验

接下来我们就通过三个核心模块来落地这套机制。

验证码图像生成(Servlet)

我们创建一个 VerifyCodeServlet,专门负责生成并输出 JPEG 图像。它被绑定到 /captcha 路径,前端只需 即可获取最新验证码。

package com.example.captcha;

import ja vax.imageio.ImageIO;
import ja vax.servlet.ServletException;
import ja vax.servlet.annotation.WebServlet;
import ja vax.servlet.http.HttpServlet;
import ja vax.servlet.http.HttpServletRequest;
import ja vax.servlet.http.HttpServletResponse;
import ja vax.servlet.http.HttpSession;
import ja va.awt.*;
import ja va.awt.image.BufferedImage;
import ja va.io.IOException;
import ja va.util.Random;

@WebServlet("/captcha")
public class VerifyCodeServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private int width = 100;
    private int height = 40;
    private int codeCount = 4;
    private int lineCount = 50;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("image/jpeg");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);

        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = image.createGraphics();

        g.setColor(Color.WHITE);
        g.fillRect(0, 0, width, height);
        g.setColor(Color.GRAY);
        g.drawRect(0, 0, width - 1, height - 1);

        Font font = new Font("Arial", Font.BOLD | Font.ITALIC, 28);
        g.setFont(font);

        char[] chars = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
                         'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U',
                         'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5',
                         '6', '7', '8', '9' };

        Random random = new Random();
        StringBuilder codeBuilder = new StringBuilder();

        // 绘制干扰线
        g.setColor(Color.LIGHT_GRAY);
        for (int i = 0; i < lineCount; i++) {
            int x1 = random.nextInt(width);
            int y1 = random.nextInt(height);
            int x2 = random.nextInt(width);
            int y2 = random.nextInt(height);
            g.drawLine(x1, y1, x2, y2);
        }

        // 绘制字符
        for (int i = 0; i < codeCount; i++) {
            String c = String.valueOf(chars[random.nextInt(chars.length)]);
            codeBuilder.append(c);
            g.setColor(new Color(random.nextInt(100), random.nextInt(100), random.nextInt(100)));
            g.drawString(c, 20 + i * 18, 28);
        }

        HttpSession session = request.getSession();
        session.setAttribute("verify_code", codeBuilder.toString().toLowerCase());

        ImageIO.write(image, "jpeg", response.getOutputStream());
        g.dispose();
    }
}

这段代码有几个值得注意的细节:

  • 使用 @WebServlet 注解简化部署配置;
  • 设置 HTTP 头禁止缓存,确保每次请求都能拿到新图;
  • 干扰线数量适中(50条),避免影响正常识别;
  • 字符集排除了易混淆的 O/0I/l/1,降低用户输错概率;
  • 颜色随机化增强 OCR 难度,但不过度花哨以免影响可读性;
  • 最终验证码转为小写存入 Session,方便后续忽略大小写校验。

小技巧:如果你希望支持中文提示词渲染,可以考虑引入 TTF 字体文件并调用 g.setFont() 加载自定义字体,这对国际化项目尤其有用。

登录页面设计(Login.jsp)

前端页面采用纯 JSP 编写,结合内联 CSS 和轻量 JS 实现良好视觉效果与交互体验。

<%@ page language="ja va" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
    String basePath = request.getScheme() + "://" +
                       request.getServerName() + ":" +
                       request.getServerPort() +
                       request.getContextPath() + "/";
%>



    
    
    用户登录
    






这里的关键在于验证码图片的动态加载机制:

  • 初始 请求生成第一张图;
  • 用户点击图片时,JS 修改 src 属性,附加时间戳参数(如 ?timestamp=171234567890);
  • 由于 URL 发生变化,浏览器不会使用缓存,从而触发 Servlet 重新生成新验证码。

这种做法简单高效,无需额外 AJAX 请求或 DOM 操作,非常适合轻量级场景。

登录逻辑处理(UserLoginServlet.ja va)

最后是登录表单的接收与验证逻辑:

package com.example.captcha;

import ja vax.servlet.ServletException;
import ja vax.servlet.annotation.WebServlet;
import ja vax.servlet.http.HttpServlet;
import ja vax.servlet.http.HttpServletRequest;
import ja vax.servlet.http.HttpServletResponse;
import ja vax.servlet.http.HttpSession;
import ja va.io.IOException;
import ja va.io.PrintWriter;

@WebServlet("/userLogin")
public class UserLoginServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        PrintWriter out = response.getWriter();
        HttpSession session = request.getSession();

        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String userInput = request.getParameter("verifyCode").toLowerCase();
        String realCode = (String) session.getAttribute("verify_code");

        if (realCode == null || !realCode.equals(userInput)) {
            out.println("");
            return;
        }

        if ("admin".equals(username) && "123456".equals(password)) {
            out.println("

✅ 登录成功!欢迎回来," + username + "

"); session.removeAttribute("verify_code"); // 防止重复使用 } else { out.println(""); } } }

几点说明:

  • 输入值统一转为小写再比对,避免因大小写导致失败;
  • 校验失败直接弹窗并返回上一页,保持上下文;
  • 成功登录后主动移除 Session 中的验证码,杜绝二次利用;
  • 当前示例使用硬编码账户,实际项目中应连接数据库查询用户信息。

安全提醒:生产环境中应使用加密存储密码(如 BCrypt),并通过 PreparedStatements 防止 SQL 注入。

可行的优化方向

虽然上述方案已能满足基本需求,但在真实项目中还可以进一步增强:

优化项 实现方式
添加噪点 在图像上绘制随机像素点,增加 OCR 识别难度
设置过期时间 web.xml 中设置 Session 超时(如 1
AJAX 提交 改用异步提交,避免整页刷新,提升 UX
返回 JSON 后端返回结构化数据,便于前后端分离架构集成
滑动/点选验证码 引入更复杂的交互式验证机制,适合高安全要求场景

例如,你可以将当前的同步表单提交改为 Fetch API 异步请求:

document.querySelector('form').addEventListener('submit', async (e) => {
    e.preventDefault();
    const formData = new FormData(e.target);
    const res = await fetch('userLogin', {
        method: 'POST',
        body: formData
    });
    const html = await res.text();
    document.body.innerHTML = html;
});

配合后端返回 JSON 响应,即可实现无刷新登录体验。

写在最后:传统验证码的未来在哪里?

我们今天实现的是典型的“图像+文本”型验证码,属于 CAPTCHA 的初级形态。随着 AI 视觉技术的发展,这类简单干扰图像正变得越来越容易被破解。

但反过来看,AI 也在催生新一代智能验证方式。比如阿里 recently 开源的 Z-Image-ComfyUI 模型,具备强大的文生图能力和语义理解水平,完全可以用来构建“智能图形挑战”:

  • “请选出包含雨伞的图片”
  • “根据描述生成指定图案”
  • “判断两幅图是否表达相同含义”

这些基于语义理解的任务,机器难以批量伪造,而人类却能轻松应对。未来的验证码可能不再是“你是不是人”,而是“你有没有人类的理解力”。

当然,在绝大多数普通业务系统中,传统的字符验证码依然够用且成本低廉。只要合理设计、及时更新策略,它仍然是性价比极高的安全防护手段。

如果你正在做一个学生管理系统、内部办公平台或小型 CMS,本文这套 JSP + Servlet 方案完全足够快速上线;

如果你关注前沿趋势,不妨尝试将 Z-Image 这类 AIGC 工具接入验证流程,探索下一代人机识别的可能性。

技术没有高低之分,只有适不适合。守住底线安全,同时保持对未来的敏感,这才是开发者应有的姿态。

本文内容来源于互联网,如有侵权请联系删除。
作者最新文章
编程开发
相关文章 更多
using namespace 使用中遇到的问题怎么解决
using namespace 使用中遇到的问题怎么解决

命名空间的基本概念与常见引入问题在C++等编程语言中,命名空间(namespace)是一种将代码标识符(如变量、函数、类名)封装在特定名称下的机制,其主要目的是避免命名冲突,尤其是在大型项目或使用多个第三方库时。使用“using namespace”指令可以将指定命名空间中的所有名称引入当前作用域,

c语言函数递归 实操经验总结:这些技巧很实用
c语言函数递归 实操经验总结:这些技巧很实用

理解递归的基本原理在C语言中,递归是一种函数调用自身的编程技术。要掌握它,首先需要理解其核心思想:将一个复杂的大问题,分解为一个或几个与原问题相似但规模更小的子问题,直到子问题足够简单,可以直接求解。这个过程通常包含两个关键部分:递归出口和递归体。递归出口定义了问题何时不再继续分解,即最简单、可直接

c语言函数递归 怎么选?常见方案对比分析
c语言函数递归 怎么选?常见方案对比分析

递归函数的基本概念与适用场景在C语言编程中,递归是一种函数调用自身的编程技巧。它并非适用于所有问题,但在处理某些具有自相似结构的问题时,能提供极其清晰和优雅的解决方案。递归的核心思想是将一个大规模问题分解为一个或多个同类型但规模更小的子问题,直到子问题简单到可以直接求解。典型的适用场景包括树形结构的

Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解
Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解

理解内存管理的基石在Objective-C的编程世界中,内存管理是开发者必须掌握的核心技能之一。它直接关系到应用的性能、稳定性与资源利用效率。与一些采用自动垃圾回收机制的语言不同,Objective-C在很长一段时间里,依赖一套基于引用计数的、需要开发者部分介入的管理规则。这套规则的核心思想是明确的

如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏
如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏

理解 dealloc 的角色与时机在 iOS 应用开发中,内存管理是保障应用性能与稳定性的基石。dealloc 方法是 Objective-C 中对象生命周期结束时的关键回调,它标志着对象即将被系统回收内存。正确理解其触发时机至关重要:当一个对象的引用计数降为零时,运行时系统会自动调用该对象的 de

深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制
深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制

内存管理的基石在Objective-C的世界里,内存管理是开发者必须掌握的核心技能之一。作为一门在手动引用计数(MRC)时代诞生的语言,Objective-C要求程序员对对象的生命周期有清晰的认识。dealloc方法正是这一生命周期中至关重要的终点站。它是一个实例方法,当对象的引用计数降为零时,系统

理解 native2ascii:Java 国际化开发中的字符编码工具
理解 native2ascii:Java 国际化开发中的字符编码工具

native2ascii 工具的基本定位在Ja va应用程序的国际化与本地化开发过程中,处理非拉丁字符集是一个常见且关键的环节。Ja va内部使用Unicode字符集来统一表示全球各种语言的文字,但其属性文件(.properties)在历史上要求使用ASCII编码,或者更准确地说,要求非ASCII字

如何使用 native2ascii 转换中文字符为 Unicode 转义序列
如何使用 native2ascii 转换中文字符为 Unicode 转义序列

理解 native2ascii 工具的基本用途在软件开发,特别是涉及国际化处理的场景中,开发者常常需要处理不同编码的文本资源。native2ascii 是 Ja va 开发工具包(JDK)中提供的一个命令行实用程序,其主要功能是将包含本地字符编码(非ASCII字符)的文件,转换为包含 Unicode

Java native2ascii 命令详解:解决属性文件乱码问题
Java native2ascii 命令详解:解决属性文件乱码问题

native2ascii 命令的由来与作用在Ja va开发中,处理国际化资源文件是一个常见需求。资源文件通常以.properties格式存储,用于支持多语言界面。然而,Ja va属性文件默认采用ISO-8859-1字符集编码,这导致了一个直接的问题:当文件中包含非拉丁字符(如中文、日文、韩文等)时,

一个 memwatch 实战案例:定位野指针问题
一个 memwatch 实战案例:定位野指针问题

内存监控工具的价值与挑战在软件开发,尤其是使用C/C++这类手动管理内存的语言时,内存错误是程序员最常遭遇的难题之一。其中,野指针问题因其隐蔽性和破坏性,往往成为最难定位的“幽灵”缺陷。它可能潜伏在代码中,在特定条件下才被触发,导致程序崩溃、数据损坏或难以预测的行为。传统的调试手段,如打印日志或使用

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件
macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

Mac软件 更多
灵活计算器
灵活计算器
macOS/iOS/Android

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

赤友清理大师
赤友清理大师
macOS

赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

WINDOWS 更多
Windows 10
Windows 10
Windows

Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

密码键盘
密码键盘
Windows/macOS/iOS/Android

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。