java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java Web验证码拦截与校验

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

作者:微笑听雨。

验证码代码用于防止机器人提交表单或访问受限页面,它是一个图像或文本序列,需要输入才能验证身份,这篇文章主要介绍了如何在Java Web项目中优雅地实现验证码拦截与校验的相关资料,需要的朋友可以参考下

前言

在 Web 开发中,很多网站都会对敏感页面或公告信息做 验证码拦截,以防止爬虫或恶意访问。

比如访问某些公告详情页时,必须先输入验证码才能继续浏览。

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

1. 需求分析

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

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

拦截逻辑:

2. Filter 拦截实现

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

public class CaptchaFilter implements Filter {

    @Override
    public 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

<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/javaee
         http://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;

    @Override
    public 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 配置");
        }
    }

    @Override
    protected 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. 常见问题处理:避免死循环,解决验证码图片缓存问题。

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

到此这篇关于如何在Java Web项目中优雅地实现验证码拦截与校验的文章就介绍到这了,更多相关Java Web验证码拦截与校验内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文