当前位置: 首页 > news >正文

在 Java Web 项目中优雅地实现验证码拦截与校验

在 Java Web 项目中优雅地实现验证码拦截与校验

在 Web 开发中,很多网站都会对敏感页面或公告信息做 验证码拦截,以防止爬虫或恶意访问。
比如访问某些公告详情页时,必须先输入验证码才能继续浏览。

本文将以一个实际的案例为例,演示如何在 Java Web 项目中优雅地实现 Filter 拦截 + 验证码校验 + 友好页面展示


1. 需求分析

假设我们的系统中有以下目录需要保护:

xzbgg、zhzzsgg、zhzzbhxr、zhzbjggs、zhzzzgg、
xfzbgg、zhfcghxr、zhfcgjg、zhfzzgg、zhzbxx

拦截逻辑:

  • /xxx/index.jhtml 这样的首页不需要拦截;
  • /xxx/12345.jhtml 这样的详情页必须输入验证码才能访问;
  • 验证码通过后,自动跳转回用户最初请求的页面。

2. Filter 拦截实现

首先我们定义一个 CaptchaFilter,利用 正则表达式 精准拦截需要验证的页面:

public class CaptchaFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletRequest req = (HttpServletRequest) request;HttpServletResponse resp = (HttpServletResponse) response;String uri = req.getRequestURI();// 定义需要保护的路径前缀String[] protectedPaths = {"xzbgg", "zhzzsgg", "zhzzbhxr", "zhzbjggs", "zhzzzgg","xfzbgg", "zhfcghxr", "zhfcgjg", "zhfzzgg", "zhzbxx"};// 匹配 /xxx/数字.jhtml 的请求String pattern = MessageFormat.format(".*/(?:{0})/\\d+\\.jhtml$", String.join("|", protectedPaths));if (uri.matches(pattern)) {HttpSession session = req.getSession();Boolean captchaPassed = (Boolean) session.getAttribute("captchaPassed");// 如果验证码已通过,放行并清理标记if (Boolean.TRUE.equals(captchaPassed)) {chain.doFilter(request, response);session.removeAttribute("captchaPassed");return;}// 保存目标 URL,便于校验成功后跳转回来session.setAttribute("targetUrl", uri);// 转发到验证码页面req.getRequestDispatcher("/captcha.html").forward(req, resp);return;}// 其他请求直接放行chain.doFilter(request, response);}
}

这样,所有访问 /xxx/数字.jhtml 的请求都会被拦截,并跳转到验证码页面。


3. web.xml 配置

接着在 web.xml 中注册 Filter 和两个 Servlet

  • 一个生成验证码图片(如 JCaptchaServlet);
  • 一个校验验证码输入(我们自定义的 CaptchaCheckServlet)。
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaeehttp://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"version="3.1"><!-- 验证码拦截器 --><filter><filter-name>CaptchaFilter</filter-name><filter-class>com.example.filter.CaptchaFilter</filter-class></filter><filter-mapping><filter-name>CaptchaFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><!-- 验证码图片生成 Servlet (例如 JCaptcha) --><servlet><servlet-name>CaptchaServlet</servlet-name><servlet-class>com.octo.captcha.module.servlet.image.SimpleImageCaptchaServlet</servlet-class></servlet><servlet-mapping><servlet-name>CaptchaServlet</servlet-name><url-pattern>/captcha.svl</url-pattern></servlet-mapping><!-- 验证码校验 Servlet --><servlet><servlet-name>CaptchaCheckServlet</servlet-name><servlet-class>com.example.servlet.CaptchaCheckServlet</servlet-class></servlet><servlet-mapping><servlet-name>CaptchaCheckServlet</servlet-name><url-pattern>/captcha/check</url-pattern></servlet-mapping>
</web-app>

4. CaptchaCheckServlet 校验逻辑

CaptchaCheckServlet 用来接收用户输入的验证码,并进行校验。
如果校验失败,重定向回 captcha.html?error=1

public class CaptchaCheckServlet extends HttpServlet {private ImageCaptchaService captchaService;@Overridepublic void init() throws ServletException {super.init();WebApplicationContext appCtx =WebApplicationContextUtils.getWebApplicationContext(getServletContext());captchaService = BeanFactoryUtils.beanOfTypeIncludingAncestors(appCtx, ImageCaptchaService.class);if (captchaService == null) {throw new ServletException("CaptchaService 未初始化,请检查 Spring 配置");}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {HttpSession session = req.getSession();String captchaId = session.getId();String inputCode = req.getParameter("code");boolean passed;try {passed = captchaService.validateResponseForID(captchaId, inputCode);} catch (Exception e) {passed = false;}if (passed) {// 验证成功,设置标记并跳转回原页面session.setAttribute("captchaPassed", true);String targetUrl = (String) session.getAttribute("targetUrl");session.removeAttribute("targetUrl");if (targetUrl != null) {resp.sendRedirect(targetUrl);} else {resp.sendRedirect(req.getContextPath() + "/");}} else {// 验证失败,重定向并附带错误标记resp.sendRedirect(req.getContextPath() + "/captcha.html?error=1");}}
}

5. 验证码页面(HTML)

验证码页面保留为 纯 HTML,通过 JavaScript 获取 URL 参数 error 来显示错误提示。

<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><title>请输入验证码</title><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>body {font-family: "Microsoft YaHei", Arial, sans-serif;display: flex;justify-content: center;align-items: center;min-height: 100vh;margin: 0;background: #f4f6f9;}.captcha-container {background: #fff;padding: 2rem;border-radius: 12px;box-shadow: 0 4px 12px rgba(0,0,0,0.1);text-align: center;width: 300px;}h2 {margin-bottom: 1.2rem;font-size: 1.2rem;color: #333;}.error {color: red;margin-bottom: 1rem;display: none;}img {cursor: pointer;margin-bottom: 1rem;border: 1px solid #ddd;border-radius: 6px;}input[type="text"] {width: 100%;padding: 10px;border: 1px solid #ccc;border-radius: 6px;font-size: 1rem;margin-bottom: 1rem;}button {width: 100%;padding: 10px;border: none;border-radius: 6px;background: #007bff;color: white;font-size: 1rem;cursor: pointer;}button:hover {background: #0056b3;}.tip {margin-top: 0.8rem;font-size: 0.9rem;color: #666;}</style>
</head>
<body>
<div class="captcha-container"><h2>请输入验证码</h2><div class="error" id="errorMsg">验证码错误,请重新输入</div><form action="/zhcms/captcha/check" method="post"><img src="/zhcms/captcha.svl"onclick="this.src='/zhcms/captcha.svl?'+Math.random()"alt="验证码" title="点击图片更换验证码"/><input type="text" name="code" placeholder="请输入验证码" required/><button type="submit">提交</button><div class="tip">看不清?点击图片刷新验证码</div></form>
</div><script>// 从 URL 获取参数const params = new URLSearchParams(window.location.search);if (params.get("error") === "1") {document.getElementById("errorMsg").style.display = "block";}
</script>
</body>
</html>

页面效果如图所示:
在这里插入图片描述


6. 常见问题 Q&A

Q1: 为什么验证码页面用 JSP 会被拦截?
A: 因为 Filter 拦截了 .jhtml 之外的请求时,转发到 JSP 也会触发过滤逻辑,容易形成死循环。改成 .html 并使用 JavaScript 处理错误提示即可。

Q2: 验证码图片不显示?
A: 检查 captcha.svl 是否正确映射到 SimpleImageCaptchaServlet。同时确认浏览器是否缓存了图片,可以通过 ?Math.random() 动态刷新。

Q3: 校验通过后没有跳转回原页面?
A: 确保在 Filter 里使用 session.setAttribute("targetUrl", uri) 保存目标地址,并在 CaptchaCheckServlet 里取出并重定向。

Q4: 如何扩展更多拦截目录?
A: 只需在 protectedPaths 数组里新增路径名即可,例如 "newpath1", "newpath2"


7. 总结

通过以上几个步骤,我们就实现了一个完整的验证码拦截机制:

  1. Filter 拦截:利用正则表达式精确拦截 /xxx/数字.jhtml 的请求;
  2. 验证码校验:用户输入后由 CaptchaCheckServlet 校验,成功后跳转回原始页面;
  3. 用户体验优化:验证码页面保持 HTML,支持错误提示,UI 简洁美观;
  4. 常见问题处理:避免死循环,解决验证码图片缓存问题。

这种实现方式简单清晰、可维护性强,非常适合需要在项目中批量拦截页面并加上验证码验证的场景。


http://www.xdnf.cn/news/1463725.html

相关文章:

  • 新闻丨重庆两江新区党工委副书记、管委会主任许宏球一行莅临华院计算考察指导
  • Java 内存模型与垃圾回收机制详解
  • 迅为RK3568开发板OpenHarmonyv3.2-Beta4版本测试-命令终端
  • AI在目前会议直播系统中应用
  • CSS 选择器的优先级/层叠性
  • watchEffect 与 watch的区别
  • 双轴倾角传感器厂家与物联网角度传感器应用全解析
  • MySQL】从零开始了解数据库开发 --- 表的操作
  • 盘点完今年CoRL最火的VLA论文,发现最强的机器人,竟是用“假数据”喂大的
  • 前端视觉交互设计全解析:从悬停高亮到多维交互体系(含代码 + 图表)
  • “我店”模式:热潮中的商机还是泡沫陷阱?深度解析当前入局可行性
  • 阿里云vs腾讯云按量付费服务器
  • 腾讯云大模型训练平台
  • BigDecimal的使用
  • 【AndroidStudio】官网下载免安装版,AndroidStudio压缩版的配置和使用
  • 华为网路设备学习-32(BGP协议 七)路由反射器与联邦
  • 中小企业数字化转型卡在哪?选对AI工具+用好企业微信,人力成本直降70%
  • SQLalachemy 错误 - Lost connection to MySQL server during query
  • 功能强大的多线程端口扫描工具,支持批量 IP 扫描、多种端口格式输入、扫描结果美化导出,适用于网络安全检测与端口监控场景
  • 基于SpringBoot的旅游管理系统的设计与实现(代码+数据库+LW)
  • 零基础直奔HCIE?先打好基础,后续才更轻松!
  • Redis 深度解析:数据结构、持久化与集群
  • 【Linux手册】动静态库:从原理到制作
  • mcp_clickhouse代码学习
  • 大彩串口屏-烧录与调试
  • 解决微信小程序开发初始化npm install包失败
  • uniapp阿里云验证码使用
  • Java观察者模式
  • 【Linux游记】基础指令篇
  • 关于CAN总线bus off 理论标准 vs 工程实践