应用Graphics2D创建滑块验证码

news/2025/11/30 16:28:45/文章来源:https://www.cnblogs.com/ljbguanli/p/19289589

应用Graphics2D创建滑块验证码

使用Graphics2D创建滑块验证码

  • 说明
  • 源码
  • 思路
    • 主要代码
  • 相关知识点
  • 注意

说明

这是一个实现滑块验证码功能的Spring Boot应用示例
环境:

  • Spring Boot 3
  • JDK 17
  • Maven 3

相关API:Graphics2D用来给原图加凹槽,裁剪形状作为滑块。

源码

gitee: https://gitee.com/qkzztx_admin/captcha-demo.git

效果:

在这里插入图片描述

思路

  1. 加载原图缩放到固定大小
  2. 生成一个Shape用作凹槽和滑块的形状区域
  3. 把Shape形状的透明黑色凹槽覆盖在原图上形成背景图
  4. 放置裁剪区域,原图覆盖在滑块图片上得到滑块图。

主要代码

  1. 创建自定义的形状
    这里仅仅创建了三种:圆形、拼图、矩形。
    坐标都是从(0,0)开始。
/**
* 创建自定义形状
* 形状初始坐标都是0,0
*/
private static Shape createCustomShape(String shapeType) {
return switch (shapeType) {
case "circle" ->
// 圆形滑块
new java.awt.geom.Ellipse2D.Float(0, 0, BLOCK_WIDTH, BLOCK_HEIGHT);
case "puzzle" ->
// 拼图形状 - 自定义复杂形状
createPuzzleShape();
case "rectangle" ->
// 矩形带圆角
new java.awt.geom.RoundRectangle2D.Float(0, 0, BLOCK_WIDTH, BLOCK_HEIGHT, 20, 20);
default -> new java.awt.geom.Rectangle2D.Float(0, 0, BLOCK_WIDTH, BLOCK_HEIGHT);
};
}
/**
* 创建拼图形状(更复杂的形状)
*/
private static Shape createPuzzleShape() {
Area area = new Area(new Rectangle(0, 0, BLOCK_WIDTH, BLOCK_HEIGHT));
// 添加凸起和凹陷
int protrusionWidth = BLOCK_WIDTH / 4;
int protrusionHeight = BLOCK_HEIGHT / 3;
// 右侧凸起
Area rightProtrusion = new Area(new Rectangle(
BLOCK_WIDTH - protrusionWidth / 2,
BLOCK_HEIGHT / 3,
protrusionWidth,
protrusionHeight
));
area.add(rightProtrusion);
// 左侧凹陷
Area leftIndentation = new Area(new Rectangle(
-protrusionWidth / 2,
BLOCK_HEIGHT / 3,
protrusionWidth,
protrusionHeight
));
area.subtract(leftIndentation);
return area;
}
  1. 创建背景图
    先绘制原图
    挪到目标位置绘制黑色透明(透明度为90)的形状凹槽
    添加边框便于识别
/**
* 创建带凹槽的背景图
*/
private static BufferedImage createBackgroundWithHole(BufferedImage background,
Shape shape, int x, int y) {
BufferedImage result = new BufferedImage(
background.getWidth(),
background.getHeight(),
BufferedImage.TYPE_INT_ARGB
);
Graphics2D g2d = result.createGraphics();
// 设置高质量渲染
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// 绘制原图
g2d.drawImage(background, 0, 0, null);
int targetX = (int) (x - shape.getBounds().getWidth() / 2);
int targetY = (int) (y - shape.getBounds().getHeight() / 2);
// 创建凹槽效果
g2d.setComposite(AlphaComposite.SrcOver);
g2d.translate(targetX, targetY);
g2d.setColor(new Color(0, 0, 0, 90));
g2d.fill(shape);
g2d.translate(-targetX, -targetY);
// 添加边框效果(可选)
g2d.setComposite(AlphaComposite.SrcOver);
g2d.setColor(new Color(255, 255, 255, 100));
g2d.setStroke(new BasicStroke(2));
g2d.translate(targetX, targetY);
g2d.draw(shape);
g2d.translate(-targetX, -targetY);
g2d.dispose();
return result;
}
  1. 创建滑块图
    滑块图的高度和背景图高度一样
    滑块图的宽度是定义的滑块宽度60, 再加上边距。
    滑块绘制的背景色是透明的。
    裁剪的逻辑是把原图覆盖在滑块上,重合点是目标点,裁剪区域就是Shape。
/**
* 创建滑块图片(滑块高度与背景图一致,宽度略大于形状,确保形状位置与凹槽一致)
*/
private static BufferedImage createSliderImage(BufferedImage background,
Shape shape, int x, int y) {
// 滑块图片宽度为形状宽度 + 边距,高度与背景相同
int sliderWidth = (int) shape.getBounds().getWidth() + BLOCK_PADDING * 2;
int bgHeight = background.getHeight();
BufferedImage slider = new BufferedImage(
sliderWidth,
bgHeight,
BufferedImage.TYPE_INT_ARGB
);
Graphics2D g2d = slider.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// 设置透明背景
g2d.setComposite(AlphaComposite.Clear);
g2d.fillRect(0, 0, sliderWidth, bgHeight);
g2d.setComposite(AlphaComposite.SrcOver);
// 计算目标位置(滑块图片水平居中)
int targetX = sliderWidth / 2;
// 创建移动后的形状区域
AffineTransform transform = AffineTransform.getTranslateInstance(
targetX - shape.getBounds().getWidth() / 2,
y - shape.getBounds().getHeight() / 2
);
Shape movedShape = transform.createTransformedShape(shape);
// 设置裁剪区域为移动后的形状
g2d.setClip(movedShape);
// 绘制背景图中对应的区域
// 计算背景图需要绘制的偏移量,使得形状区域对齐
int drawX = targetX - x;
g2d.drawImage(background, drawX, 0, null);
g2d.dispose();
return slider;
}
  1. 返回值
    验证的坐标点是横坐标:目标点-边距-滑块宽度一半
    和前端放置滑块的位置有关
    和前端放置背景图的宽度有关
private CaptchaInfo generateSliderCaptcha(BufferedImage background,
int x, int y,
String shapeType) {
// 创建形状
Shape sliderShape = createCustomShape(shapeType);
// 生成带凹槽的背景图
BufferedImage bgWithHole = createBackgroundWithHole(background, sliderShape, x, y);
// 生成滑块图片(高度与背景相同)
BufferedImage sliderImage = createSliderImage(background, sliderShape, x, y);
// 生成唯一令牌作为sessionId
String sessionId = generateSessionId();
// 保存验证码会话信息
captchaSessions.put(sessionId, new CaptchaSession((int) (x - BLOCK_PADDING - sliderShape.getBounds().getWidth() / 2), y));
log.info("生成验证码,sessionId: {}, x: {}, y: {}, shapeWidth: {}, padding: {}, sliderWidth: {}, backgroundWidth: {}",
sessionId, x, y, sliderShape.getBounds().getWidth(), BLOCK_PADDING, sliderImage.getWidth(), bgWithHole.getWidth());
// 封装验证码信息
CaptchaInfo captchaInfo = new CaptchaInfo();
captchaInfo.setBackground(imageToBase64(bgWithHole));
captchaInfo.setSlider(imageToBase64(sliderImage));
captchaInfo.setSessionId(sessionId);
return captchaInfo;
}

相关知识点

  1. Graphics2D的坐标系
  • 左上角为0,0,向右是x轴正方向,向下是y轴正方向
  • 绘制图形Shape是,放置点是创建Shape的坐标点,上面的代码都是0,0点。
  • Shape的锚点是在Shape的左上角。
  • 也就是绘制图形时,对准的点是Shape(x,y)和当前坐标系的(x,y)
  • 坐标系原点是可以移动的,api是Graphics2D实例的translate方法。
  1. Shape 坐标创建后需要创建一个新的移动
    代码
// 创建移动后的形状区域
AffineTransform transform = AffineTransform.getTranslateInstance(
targetX - shape.getBounds().getWidth() / 2,
y - shape.getBounds().getHeight() / 2
);
Shape movedShape = transform.createTransformedShape(shape);

注意

  1. 这里默认策略是保持背景图和滑块图高度一样,就不用验证y坐标了
  2. 验证坐标时要考虑前端展示容器的宽高,要么前端传容器高度验证,后端用比例验证,要么后端把容器高度传给前端。
  3. 随机目标点时,是在背景图的1/4到3/4的范围内,保证滑块需要滑动才可以到凹槽,且凹槽不超出背景图。
  4. 原图的位置可以放到任何位置,源码、数据库、oss、其他服务等等。
  5. 安全性是系统中安全模块或者服务的事情。
  6. 根据以上滑块的验证码,可以做成二维的,拖动的,多个滑块,形状也可以自定义,还可以维护起来做成动态配置的。
  7. index.html中的验证逻辑是deepseek写的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

母婴商标购买平台实测 TOP 榜公布(2025版):这 5 家安全过户不踩雷

在母婴行业品牌化竞争白热化的 2025 年,一枚合规适配的商标成为企业入驻电商平台、建立消费者信任的 “关键门槛”。母婴类商标因覆盖食品、洗护、玩具、服饰等多个细分品类,对分类精准度、合规性及过户效率的要求远…

分子级的管理智慧:哲讯科技以SAP重塑化工行业安全与效能新标杆

分子级的管理智慧:哲讯科技以SAP重塑化工行业安全与效能新标杆化工行业,是现代工业体系的基石,从日常生活的衣食住行到关乎国计民生的高端材料,其身影无处不在。然而,这个创造无限可能的行业,也始终伴随着复杂的…

NOI Plus 2025 游记

T1 对于这种简单题,我们可以使用贪心解决。 对于这种简单的反悔贪心,我们显然是可以用网络流来做的。于是我开始思考费用流。 我开始建立费用流模型。对于每种商品建立两个点,源点向其中一个点连一条流量为无限,费…

2025赣州实力会议会展酒店TOP5权威推荐:专业场地赋能商

近年来,赣州作为区域经济与文旅融合的核心城市,商务会议、政务接待与大型庆典需求持续增长。2024年数据显示,赣州酒店会议会展业务年增速超25%,但行业痛点凸显:超30%的客户投诉集中在场地容量适配不足、设备老旧、…

Animation Rigging Unity官方的IK动画绑定教程

简单调整人物手臂IK看这个视频(5分钟): [Unity教程]-动画绑定(Animation Rigging)_哔哩哔哩_bilibili unity官方教程: [Unity 活动]-官方直播- Unity最新程序化动画绑定(Animation Rigging)技术介绍_哔哩哔哩_bilibi…

2025年河北实力不错的西点学校排名:西点学校哪家权威?西点

本榜单依托石家庄、保定、邯郸等河北核心城市的市场调研与真实学员口碑,深度筛选出5家标杆西点学校,为热爱西点的人群提供客观选型依据,助力精准匹配专业学习平台。 TOP1 推荐:河北欧米奇西点西餐学校 推荐指数:…

智链芯未来:哲讯科技以SAP系统赋能半导体产业数字化变革

智链芯未来:哲讯科技以SAP系统赋能半导体产业数字化变革在全球化竞争与技术迭代加速的背景下,半导体行业正面临前所未有的机遇与挑战。从芯片设计、晶圆制造到封装测试,产业链的复杂性与协同难度持续攀升。如何实现…

DDD支付模块

工作中对接了招商银行模块,但是回调过程中需要考虑很多问题,这里小计一下 网络不可靠!可能出现: 你的服务器临时过载(GC、Full GC) 数据库连接池满 防火墙拦截 代码 bug 导致 500 机房网络抖动 系统必须支持 幂等处…

实用指南:自然语言处理NLP的数据预处理:从原始文本到模型输入(MindSpore版)

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

mac 防止brew 安装 nginx 后不通过服务直接启动

直接看代码~,逻辑很简单,把原命令替换掉NGINX_BIN=$(which nginx) sudo mv "$NGINX_BIN" "${NGINX_BIN}.bin" sudo tee "$NGINX_BIN" << EOF #!/bin/bashNGINX_REAL="${B…

2025年云南高三高考冲刺培训排名:高考冲刺培训推荐几家?

TOP1 推荐:昆明市五华区滇云教育培训学校有限公司 推荐指数:★★★★★ 口碑评分:云南家长公认的靠谱高考冲刺机构 专业能力:滇云教育是经五华区教体局批准的正规培训机构,以16年高考补习经验为根基,构建精细化管…

从小工到专家3

篇三:《终身成长:重新定义成功的思维模式》读后感 —— 专家的格局:用 “成长思维” 抵御职业天花板​ 成为行业内公认的 “专家” 后,我曾一度陷入 “能力固化” 的焦虑:担心自己的知识体系过时,害怕被新生代超…

2025常州本地美食新地标TOP5权威推荐:挖掘城市烟火味,

在消费升级与城市文旅融合的浪潮下,本地美食新地标已成为城市文化传播与消费活力的核心载体。2024年常州餐饮消费数据显示,美食打卡地带动的餐饮客流量年增35%,但消费者常遇特色同质化体验单一化文化割裂化三大痛点…

CICD(一)CI/CD概述及GitLab部署和一些Git命令 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

11.30代码大全二

一、核心认知:代码的本质是“与人沟通” 很多人误以为代码是写给计算机执行的,实则代码首先是写给人看的,其次才是给机器运行的——毕竟代码的生命周期中,编写时间仅占10%,其余90%都在阅读、修改、调试和维护。这…

KFCoder - 敏捷冲刺日志 - 3rd

第 3 篇 Scrum 冲刺博客(2025 年 11 月 29 日) 一、站立式会议记录项目 内容会议时间 2025 年 11 月 29 日 09:00-09:13(13 分钟)会议地点 团队参会人员 徐新曜、许国伟、罗芷忻、田璐、陈曦、白子璇、辜艺淇会议照…

11.30代码大全二(2)

二、关键原则与实践保持代码清晰可读命名是最重要的事:变量、函数、类名应准确表达其用途,避免缩写(除非广泛认可)。例如,用 CalculateTotalPrice() 而非 CalcTP() 。 ​ 注释应解释“为什么”而非“是什么”:…

KFCoder - 敏捷冲刺日志 - 2nd

第 2 篇 Scrum 冲刺博客(2025 年 11 月 28 日) 一、站立式会议记录项目 内容会议时间 2025 年 11 月 28 日 09:00-09:12(12 分钟)会议地点 团队参会人员 徐新曜、许国伟、罗芷忻、田璐、陈曦、白子璇、辜艺淇会议照…

2025江苏塑料中空板厂家TOP5权威推荐:中空板咬盖箱/对

包装领域中,企业对定制化塑料中空板制品需求日益增长。2024年数据显示,国内中空板包装市场规模超300亿元,年增速达28%,但30%的投诉集中在产品精度不足、防静电性能不达标、定制周期长三大问题。企业采购常遇坑:电…

2025专业奢侈品回收平台TOP5推荐:综合口碑企业助力安全

奢侈品回收市场需求逐年攀升,但价格不透明、交易周期长、异地回收难等痛点长期困扰用户。2023年行业调研显示,超60%用户遭遇过报价虚高后压价打款延迟等问题。为帮助用户避开回收坑,本文基于鉴定专业度、回款效率、…