实现Java后端的图形验证码和行为验证码

登录添加图形验证码:

Java 中,我们可以使用一些图形处理库(如 java.awtjavax.imageio)生成图形验证码,并将验证码文本存储在会话(session)中以供验证。下面是一个完整的实现步骤,包括 验证码图片生成验证码接口、以及 验证逻辑


1. Maven 依赖(可选)

如需引入依赖管理,可以使用 Maven 项目结构。示例中我们主要依赖 Java 自带的 javax 库,所以无需添加额外依赖。

如果你需要更多验证码功能,kaptcha 是一个常用的 Java 验证码生成库,可以通过 Maven 添加:

xml
<dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>2.3.2</version>
</dependency>

2. 验证码生成与接口实现

下面是一个使用 javax.imageiojava.awt 库生成验证码图片的例子,并通过 Servlet 提供 HTTP 接口。

生成验证码的 Java Servlet

import javax.imageio.ImageIO;
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.servlet.http.HttpSession;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

@WebServlet("/captcha")
public class CaptchaServlet extends HttpServlet {

    private static final int WIDTH = 160;
    private static final int HEIGHT = 40;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 创建验证码图片
        BufferedImage captchaImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = (Graphics2D) captchaImage.getGraphics();
        Random random = new Random();

        // 生成随机验证码文本
        String captchaText = generateCaptchaText(6);
        HttpSession session = request.getSession();
        session.setAttribute("captcha", captchaText); // 将验证码存储在 session 中

        // 设置图片背景颜色
        g.setColor(Color.LIGHT_GRAY);
        g.fillRect(0, 0, WIDTH, HEIGHT);

        // 设置字体与颜色
        g.setFont(new Font("Arial", Font.BOLD, 30));
        g.setColor(Color.BLACK);
        g.drawString(captchaText, 20, 30);

        // 画干扰线
        for (int i = 0; i < 5; i++) {
            g.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255)));
            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);
        }

        g.dispose(); // 释放资源

        // 设置响应类型为图片格式
        response.setContentType("image/png");
        ImageIO.write(captchaImage, "png", response.getOutputStream()); // 输出图片
    }

    // 生成随机验证码文本
    private String generateCaptchaText(int length) {
        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            sb.append(chars.charAt(random.nextInt(chars.length())));
        }
        return sb.toString();
    }
}

解释:

  1. CaptchaServlet
    • 使用 BufferedImage 创建验证码图片。
    • 使用 Graphics2D 在图片上绘制文字和干扰线。
    • 验证码文本存储在 session 中,以便后续验证。
  1. 会话存储session.setAttribute("captcha", captchaText); 将验证码保存到用户会话中。
  2. 图片响应ImageIO.write(captchaImage, "png", response.getOutputStream()); 将图片直接写入 HTTP 响应流中。

3. 验证码验证逻辑

在用户提交表单时,后端需要验证用户输入的验证码与 session 中存储的验证码是否一致。

登录接口示例

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.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/login")
public class LoginServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String userAccount = request.getParameter("userAccount");
        String userPassword = request.getParameter("userPassword");
        String captchaInput = request.getParameter("captcha");

        HttpSession session = request.getSession();
        String captcha = (String) session.getAttribute("captcha");

        // 验证验证码是否正确
        if (captcha == null || !captcha.equalsIgnoreCase(captchaInput)) {
            response.getWriter().write("验证码错误");
            return;
        }

        // TODO: 验证账号和密码的逻辑
        if ("admin".equals(userAccount) && "password".equals(userPassword)) {
            response.getWriter().write("登录成功");
        } else {
            response.getWriter().write("账号或密码错误");
        }
    }
}

解释:

  1. 验证码验证:获取用户输入的验证码,并与会话中的验证码进行比对。
  2. 忽略大小写:使用 equalsIgnoreCase 忽略大小写进行比较。
  3. 验证逻辑:如果验证码正确且账号密码验证成功,则返回“登录成功”。

4. 前端代码:提交表单与验证码显示

前端代码需要请求验证码图片,并将验证码输入框的内容一并提交给后端。

HTML 示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登录页面</title>
</head>
<body>
    <form action="/login" method="POST">
        <input type="text" name="userAccount" placeholder="请输入账号" required />
        <input type="password" name="userPassword" placeholder="请输入密码" required />
        <img src="/captcha" alt="验证码" onclick="this.src='/captcha?'+Math.random()" />
        <input type="text" name="captcha" placeholder="请输入验证码" required />
        <button type="submit">登录</button>
    </form>
</body>
</html>

解释:

  1. 验证码刷新:点击验证码图片时,通过 this.src='/captcha?' + Math.random() 强制刷新验证码。
  2. 表单提交:在用户填写验证码后,将表单提交到 /login 接口。

使用行为验证码:

在开发中添加行为验证码是一种有效的防止自动化攻击和机器人行为的方式。以下是一些免费且公开可用的行为验证码解决方案:

1. hCaptcha

  • 概述:hCaptcha 是一个免费的验证码服务,提供用户交互验证,防止恶意自动化。
  • 特点
    • 简单易用,与 Google reCAPTCHA 类似。
    • 提供奖励系统,网站可以通过用户完成验证获取收益。
  • 使用
    • 访问 hCaptcha官网 注册并获取 API 密钥。
    • 根据文档进行集成。

2. Google reCAPTCHA

  • 概述:虽然 reCAPTCHA 主要是为识别机器人设计的,但其 "Checkbox" 和 "Invisible" 选项可以用作行为验证码。
  • 特点
    • 广泛使用,用户熟悉。
    • 提供多种验证方式,包括隐形验证。
  • 使用
    • 注册 Google reCAPTCHA 获取密钥。
    • 按照文档进行集成。

3. Friendly Captcha

  • 概述:Friendly Captcha 提供了一种不依赖于用户交互的验证码系统,适合于保护用户隐私。
  • 特点
    • 不需要用户填写验证码,用户体验更佳。
    • 通过计算资源验证用户行为。
  • 使用
    • 访问 FriendlyCaptcha官网 注册并获取 API 密钥。
    • 根据文档进行集成。

实战hCaptcha:

在vue项目中创建组件:

<template>
  <div>
    <!-- hCaptcha 容器 -->
    <div
      ref="hcaptchaContainer"
      class="h-captcha"
      :data-sitekey="siteKey"
    ></div>
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref, defineEmits } from "vue";
const token = ref<string | null>(null); // 存储验证 token

// 定义事件,向父组件发送 token
const emit = defineEmits(["verify"]);
interface Props {
  siteKey: string;
}

// const props = defineProps<Props>(); // 接收 siteKey 作为参数
const hcaptchaContainer = ref<HTMLDivElement | null>(null); // hCaptcha 容器的引用

// 生命周期钩子,确保 hCaptcha 在组件挂载后初始化
onMounted(() => {
  if (window.hcaptcha) {
    console.log("hCaptcha 已加载");
    window.hcaptcha.render(hcaptchaContainer.value!, {
      sitekey: "10000000-ffff-ffff-ffff-000000000001",
      callback: onVerify,
      // sitekey: "fbc8723c-c356-49d6-a524-bf327f9ac81a",
    });
  } else {
    console.error("hCaptcha 脚本未加载");
  }
});

const onVerify = (response: string) => {
  console.log("hCaptcha 验证成功,token:", response);
  emit("verify", response); // 通过事件发送 token 给父组件
};
</script>

<style>
.h-captcha {
  margin: 10px 0;
}
</style>

然后在登录页面中使用,并且进行拦截:

<template>
  <div id="userLoginView">
    <h1 style="margin-bottom: 56px; color: #fff">用户登录</h1>
    <a-form
      style="
        display: flex;
        flex-direction: column;
        justify-content: space-between;
        max-width: 520px;
        min-height: 350px;
        margin: 0 auto;
        background: rgba(255, 255, 255, 0.9);
        padding: 20px;
        border-radius: 10px;
        box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
      "
      label-align="left"
      auto-label-width
      :model="form"
      @submit="handleSubmit"
    >
      <a-form-item field="userAccount" label="账号" style="margin-top: 40px">
        <a-input v-model="form.userAccount" placeholder="请输入账号" />
      </a-form-item>
      <a-form-item field="userPassword" tooltip="密码不少于 8 位" label="密码">
        <a-input-password
          v-model="form.userPassword"
          placeholder="请输入密码"
        />
      </a-form-item>
      <HCaptcha @verify="onCaptchaVerify" />
      <a-form-item style="margin-bottom: 40px">
        <a-button
          type="primary"
          html-type="submit"
          style="width: 120px; height: 40px; margin-right: 20%"
        >
          登录
        </a-button>
        <a-button
          type="primary"
          @click="turnToRegister()"
          style="width: 120px; height: 40px; margin-left: 20%"
          >新用户注册
        </a-button>
      </a-form-item>
    </a-form>
  </div>
</template>

<script setup lang="ts">
import HCaptcha from "../../components/HCaptcha.vue";
import { reactive, ref } from "vue";
import { UserControllerService, UserLoginRequest } from "../../../generated";
import message from "@arco-design/web-vue/es/message";
import { useRouter } from "vue-router";
import { useStore } from "vuex";

/**
 * 表单信息
 */
const form = reactive({
  userAccount: "",
  userPassword: "",
} as UserLoginRequest);

const router = useRouter();
const store = useStore();

const captchaToken = ref<string | null>(null); // 存储 hCaptcha 的 token
const onCaptchaVerify = (token: string) => {
  captchaToken.value = token; // 接收 hCaptcha 的 token
};

/**
 * 提交表单
 * @param data
 */
const handleSubmit = async () => {
  if (!captchaToken.value) {
    message.error("请先通过验证码验证");
    return; // 阻止提交
  }
  const res = await UserControllerService.userLoginUsingPost(form);
  // 登录成功,跳转到主页
  if (res.code === 0) {
    await store.dispatch("user/getLoginUser");
    router.push({
      path: "/",
      replace: true,
    });
  } else {
    message.error("登陆失败," + res.message);
  }
};

/**
 * 跳转到注册页面
 */
const turnToRegister = () => {
  router.push({
    path: "/user/register",
    replace: true,
  });
};
</script>
<style>
#userLoginView {
  margin-top: 20vh;
  /*margin-left: 40vh;*/
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/904985.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

旺季来临,沃尔玛下了血本和亚马逊竞争,将会员年费减半至49美元

沃尔玛于10月28日宣布&#xff0c;在假日季到来之前推出Walmart Plus会员服务&#xff0c;以50%的折扣缩小与竞争对手亚马逊Prime订阅服务之间的差距。 为了吸引正在应对高通胀的消费者&#xff0c;今年沃尔玛和其他美国品牌方提前推出促销活动&#xff0c;并增加更多优惠和折…

1-位置:重新思考后处理的基于搜索的神经方法在解决大规模旅行商问题中的应用(arXiv 2024)

文章目录 Abstract1. Introduction2. Related Work2.1.监督学习2.2.无监督学习2.3.强化学习3. Preliminaries3.1. Problem Definition3.2.热图产生3.3.蒙特卡洛树搜索4. 提出的基线方法4.1. Motivation4.2. SoftDist基线方法5. 提出的度量方法5.1. 动机5.2. Score度量方法6. Ex…

[vulnhub] SecTalks:BNE0x00 - Minotaur

https://www.vulnhub.com/entry/sectalks-bne0x00-minotaur,139/ 主机发现端口扫描 使用nmap扫描网段类存活主机 因为靶机是我最后添加的&#xff0c;所以靶机IP是172 nmap -sP 192.168.75.0/24 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-30 15:36 CST Nmap scan…

回溯算法-Java【力扣】【算法学习day.14】

前言 ###我做这类文档一个重要的目的还是给正在学习的大家提供方向&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非常非常高滴&am…

uniapp和vite项目配置多环境编译,增加测试环境变量配置--mode test

如果你的项目是使用vite和uniapp配置开发的&#xff0c;就可以在代码里面获取到这些变量&#xff0c;但是开发&#xff0c;测试和发布是不同的请求地址&#xff0c;所以需要配置。Vite 使用 dotenv 从你的 环境目录 中的下列文件加载额外的环境变量&#xff1a; .env …

CUDA环境安装终极指南——Linux(其它系统也一样)

文章目录 前言检查驱动配置nvcc安装cudnn完活 前言 不用看其它文章了&#xff0c;这篇文章保你不踩任何坑&#xff0c;安装方法简单快速 检查驱动 检查驱动是否安装&#xff0c;输入以下命令 nvidia-smi如果驱动已经安装&#xff0c;则可跳过此步&#xff0c;否则&#xff…

学习笔记:ElasticSearch搜索引擎

学习视频&#xff1a;【尚硅谷】ElasticSearch教程入门到精通&#xff08;基于ELK技术栈elasticsearch 7.x8.x新特性&#xff09; 学习笔记&#xff1a;Elasticsearch学习笔记 目录 第1章 Elasticsearch概述01. 开篇02. 技术选型 2. 第二章 ElasticSearch入门03. 环境准备04. …

工业协议网关:物联网时代的智慧桥梁

在物联网技术蓬勃发展的今天&#xff0c;工业协议网关作为连接工业设备和物联网系统的关键设备&#xff0c;正在发挥着越来越重要的作用。本文将带您深入了解工业协议网关的功能、应用场景以及它在工业智能化进程中的重要作用。 什么是工业协议网关&#xff1f; 工业协议网关…

机器学习中的嵌入是什么?

一、说明 嵌入是真实世界对象的数字表示&#xff0c;机器学习&#xff08;ML&#xff09;和人工智能&#xff08;AI&#xff09;系统利用它来像人类一样理解复杂的知识领域。例如&#xff0c;计算算法了解 2 和 3 之间的差为 1&#xff0c;这表明与 2 和 100 相比&#xff0c;2…

Python | Leetcode Python题解之第517题超级洗衣机

题目&#xff1a; 题解&#xff1a; class Solution:def findMinMoves(self, machines: List[int]) -> int:tot sum(machines)n len(machines)if tot % n:return -1avg tot // nans, s 0, 0for num in machines:num - avgs numans max(ans, abs(s), num)return ans

【PTA】4-1 计算二叉树最大的宽度 【数据结构】

二叉树的最大宽度是指二叉树所有层中结点个数的最大值。例如&#xff1a;下面二叉树的宽度为4. 输入二叉树的完全前序序列建立一棵二叉树&#xff08;上机作业2&#xff1a;二叉树的建立和遍历&#xff09;&#xff0c;编写算法计算并输出二叉树的宽度。 输入格式: 二叉树数据…

开源智能文档处理系统,助力医疗数据精准管理与高效整合

问题导向&#xff1a; 当前医疗文档中信息零散、数据整合度低&#xff0c;导致人工管理难度加大&#xff0c;错误率高。思通数科的系统在此背景下提供了免费的开源工具&#xff0c;帮助医疗机构实现数据的高效、精准管理&#xff0c;支持实时数据提取和智能管理。 客户案例&am…

STM32使用串口下载程序

STM32使用串口下载程序 FluMcu软件下载地址 单片机在线编程网 STM32 MCU启动模式配置(Boot Configuration) 单片机复位后&#xff0c;SYSCLK的第4个上升沿&#xff0c;BOOT引脚上的值将锁存&#xff0c;用户可以通过设置BOOT0和BOOT1引脚的值&#xff0c;来选择复位后的启动…

新兴斗篷cloak技术,你了解吗?

随着互联网技术的飞速发展&#xff0c;网络营销领域也经历了翻天覆地的变革。 从最早的网络横幅广告到如今主流的搜索引擎和社交媒体营销&#xff0c;广告形式变得越来越多样。 其中&#xff0c;搜索引擎广告一直以其精准投放而备受青睐&#xff0c;但近年来&#xff0c;一项名…

WPF+MVVM案例实战(十四)- 封装一个自定义消息弹窗控件(下)

文章目录 1、案例效果2、弹窗空间使用1.引入用户控件2、按钮命令实现 3、总结4、源代码获取 1、案例效果 2、弹窗空间使用 1.引入用户控件 打开 Wpf_Examples 项目&#xff0c;在引用中添加用户控件库&#xff0c;在 MainWindow.xaml 界面引用控件库&#xff0c;代码如下&…

教材管理系统设计与实现

教材管理系统设计与实现 1. 系统概述 教材管理系统是一个基于PHP和SQL的Web应用程序&#xff0c;旨在为学校提供一个高效的教材管理平台。该系统可以帮助管理员录入教材信息、教师查询和申请教材、学生查询教材信息&#xff0c;提高教材管理的效率和透明度。 2. 技术栈 前端…

【时间序列分析】平稳时间序列分析——Wold分解定理和延迟算子

Wold分解定理 &#xff08;这个定理是平稳时间序列分析的理论基石。&#xff09; 对于任意一个离散平稳时间序列, 它都可以分解为两个不相关的平稳序列之和, 其中一个为确定性的 (deterministic), 另一个为随机性的(stochastic) xₜVₜξₜ&#xff0c;{V₁} 为确定性平稳序列…

基于SpringBoot的汽车配件销售管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

数图携手黄商集团,打造品类空间精细化管理体系!

数图合作伙伴又1 在这秋高气爽的时节&#xff0c;满怀激情地传递着喜人的消息&#xff1a;数图的合作伙伴队伍再次壮大。位于湖北黄冈的黄商集团&#xff0c;勇于拥抱时代发展的数字变革潮流&#xff0c;积极致力于探索精细化的品类空间管理之道&#xff0c;一步一个脚印&…

大模型日报|3 篇必读的大模型论文

大家好&#xff0c;今日必读的大模型论文来啦&#xff01; 1.SocialGPT&#xff1a;贪婪分段提示优化实现社会关系推理 社会关系推理旨在从图像中识别朋友、配偶和同事等关系类别。虽然目前的方法采用了使用标注图像数据端到端训练专用网络的模式&#xff0c;但这些方法在通用…