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

应用Graphics2D创建滑块验证码

应用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.jsqmd.com/news/56173/

相关文章:

  • 分子级的管理智慧:哲讯科技以SAP重塑化工行业安全与效能新标杆
  • NOI Plus 2025 游记
  • 2025赣州实力会议会展酒店TOP5权威推荐:专业场地赋能商
  • Animation Rigging Unity官方的IK动画绑定教程
  • 2025年河北实力不错的西点学校排名:西点学校哪家权威?西点
  • mac 防止brew 安装 nginx 后不通过服务直接启动
  • 从小工到专家3
  • 2025常州本地美食新地标TOP5权威推荐:挖掘城市烟火味,
  • CICD(一)CI/CD概述及GitLab部署和一些Git命令 - 详解
  • 11.30代码大全二
  • KFCoder - 敏捷冲刺日志 - 3rd
  • KFCoder - 敏捷冲刺日志 - 2nd
  • 2025江苏塑料中空板厂家TOP5权威推荐:中空板咬盖箱/对
  • 2025专业奢侈品回收平台TOP5推荐:综合口碑企业助力安全
  • 同步带轮厂家TOP5权威推荐:同步带轮制造厂甄选指南,专业的
  • 2025常州本地人推荐美食:非遗美食鲜珍珍雪山草鸡火锅,解锁
  • 代码大全阅读笔记4
  • 20232317 2025-2026-1 《网络与系统攻防技术》实验七实验报告
  • 笔记三
  • 笔记二
  • 2025年新能源汽车充电桩生产商哪家好?新能源汽车充电桩生产
  • 2025年专业的奢侈品回收品牌企业推荐:高性价比、口碑好的奢
  • 2025广东安徽山东甲级资质工程设计公司合作加盟分公司TOP
  • 2025年全国奢侈品回收平台推荐:诚信的奢侈品回收公司有哪些
  • 深入解析:【基于one-loop-per-thread的高并发服务器】--- 项目介绍模块划分
  • 2025年江西安徽甲级资质工程设计公司合作加盟分公司排行榜,
  • 完整教程:Springboot的民宿管理系统的设计与实现29rhm9uh(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 2025年十大口碑好的文艺演出公司推荐,专业有实力的文艺展示
  • AI_Info_Gemini3
  • 加训目录