前后端分离java开发图形验证码+谷歌开源Kaptcha使用(Springboot+redis实现图形验证码校验)

文章目录

  • 1.背景
    • 1.1 什么是短信-邮箱轰炸机
    • 1.2 公司带来的损失
  • 2.如何避免自己的网站成为"肉鸡“或者被刷呢
  • 3.表单重复提交问题
  • 4.Kaptcha框架介绍
  • 5.前后端分离验证码实战开发-思路分析
  • 6.前后端分离验证码实战开发-后端代码
    • 5.1 pom.xml核心依赖
    • 5.2 KaptchaConfig配置类
    • 5.3 CaptchaServiceController生成与获取验证码
    • 5.4 修改密码验证码校验代码
    • 5.5 测试图片验证码生成
  • 7.验证码未来发展的讨论

1.背景

注册-登录-修改密码一般需要发送验证码,但是容易被攻击恶意调用。

1.1 什么是短信-邮箱轰炸机

手机短信轰炸机是批量、循环给手机无限发送各种网站的注册验证码短信的方法。

1.2 公司带来的损失

短信一条5分钱,如果被大盗刷大家自己计算邮箱通知不用钱,但被大盗刷,带宽、连接等都被占用,导致无法正常使用。

2.如何避免自己的网站成为"肉鸡“或者被刷呢

  • 增加图形验证码(开发人员)

    图形验证码(CAPTCHA),是Completely Automated Public Turing Test to Tell Computers and Humans Apart (全自动区分计算机和人类的图灵测试)的简称。其本质是-种区分用户是计算机还是人的公共全自动程序。可以有效的防止某些特定程序以暴力方式不断进行登录尝试。验证码的不断发展其实是随着其破解功能的逐步强大而跟着演进的,这是一个攻防博弈的精彩世界。

  • 单IP请求次数限制(开发人员)

  • 限制号码发送(一般短信提供商会做)

  • 攻防永远是有的,只过加大了攻击者的成本,ROI划不过来自然就放弃了

3.表单重复提交问题

  1. 用户正常提交,由于网络延迟等原因,未收到服务器的响应,这时,用户着急多点了几次提交操作,会造成表单重复提交。
  2. 用户正常提交,服务器也没有延迟,但是提交完成后,用户回退浏览器。重新提交,也会造成表单重复提交。

这两个表单重复提交问题的解决方案:验证码

4.Kaptcha框架介绍

谷歌开源的一个可高度配置的实用验证码生成工具:

  • 验证码的字体/大小颜色
  • 验证码内容的范围(数字,字母,中文汉字!)
  • 验证码图片的大小,边框,边框粗细,边框颜色
  • 验证码的干扰线验证码的样式(鱼眼样式、3D、 普通模糊)

5.前后端分离验证码实战开发-思路分析

这里以修改密码为例,规则是修改用户密码必须一同将验证码传给后端,如果验证码失效或者错误都需要重新生成并获取验证码。

  1. 前端向后端请求验证码。

  2. 后端通过谷歌开源工具Kaptcha生成图形验证码(实际是5个随机字符),缓存到redis,key键可以是 业务+用户id,value值就是那5个随机字符。设置TTL为2分钟。

  3. 后端将图形验证码转base64编码字符串,将该字符串返回给前端。

  4. 前端解析base64编码的字符串,即可在页面上显示图形验证码。

  5. 用户输入密码与验证码后提交表单到后端。

  6. 后端根据业务和用户id组成的key键到redis查找缓存的验证码信息,会有如下情况:

    • 如果redis返回为空,则通知前端验证码失效,需要重新获取验证码。

    • 如果redis返回不为空,但是不相等,说明验证码输入错误。则删除redis中对应验证码缓存,通知前端验证码错误,需要重新获取验证码。

    • 如果redis返回不为空,并且相等,则校验成功,就删除redis中对应验证码缓存,并在mysql中修改密码。最后通知前端修改成功。

6.前后端分离验证码实战开发-后端代码

5.1 pom.xml核心依赖

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

5.2 KaptchaConfig配置类

package com.zhulang.waveedu.sms.config;import com.google.code.kaptcha.Producer;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.Properties;/*** 谷歌提供的图片验证码kaptcha** @author 狐狸半面添* @create 2023-01-29 11:02*/
@Configuration
public class KaptchaConfig {@Beanpublic Producer kaptcha() {Properties properties = new Properties();/*设置图片有边框并且为蓝色properties.setProperty("kaptcha.border", "no");properties.setProperty("kaptcha.border.color", "blue");*/// 设置图片无边框properties.setProperty("kaptcha.border", "no");// 背景颜色渐变开始,这里设置的是rgb值156,156,156properties.put("kaptcha.background.clear.from", "156,156,156");// 背景颜色渐变结束,这里设置以白色结束properties.put("kaptcha.background.clear.to", "white");// 字体颜色,这里设置为黑色properties.put("kaptcha.textproducer.font.color", "black");// 文字间隔,这里设置为10pxproperties.put("kaptcha.textproducer.char.space", "10");/*如果需要去掉干扰线,则如此配置:properties.put("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");*/// 干扰线颜色配置,这里设置成了idea的Darcula主题的背景色properties.put("kaptcha.noise.color", "43,43,43");// 字体properties.put("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");// 图片宽度,默认也是200pxproperties.setProperty("kaptcha.image.width", "200");// 图片高度,默认也是50pxproperties.setProperty("kaptcha.image.height", "50");// 从哪些字符中产生properties.setProperty("kaptcha.textproducer.char.string", "0123456789abcdefghijklmnopqrsduvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");// 字符个数properties.setProperty("kaptcha.textproducer.char.length", "5");Config config = new Config(properties);DefaultKaptcha defaultKaptcha = new DefaultKaptcha();defaultKaptcha.setConfig(config);return defaultKaptcha;}
}

5.3 CaptchaServiceController生成与获取验证码

  • RedisCacheUtils是一个自定义的redis缓存工具类
  • RedisConstants是一个redis常量类,主要是设置缓存key前缀与TTL
  • UserHolderUtils.getUserId():通过自定义UserHolderUtils工具类获取用户id(通过token从redis获取用户信息,包括了userId)
package com.zhulang.waveedu.sms.controller;import com.google.code.kaptcha.Producer;
import com.zhulang.waveedu.common.constant.RedisConstants;
import com.zhulang.waveedu.common.entity.Result;
import com.zhulang.waveedu.common.util.RedisCacheUtils;
import com.zhulang.waveedu.common.util.UserHolderUtils;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;/*** 生成图片验证码** @author 狐狸半面添* @create 2023-01-29 17:35*/
@RestController
@RequestMapping("/captcha")
public class CaptchaServiceController {@Resourceprivate Producer producer;@Resourceprivate RedisCacheUtils redisCacheUtils;/*** 获取修改密码的图片验证码base64编码* 并缓存至redis中** @return base编码*/@GetMapping("/pwd/vc.jpg")public Result getPwdCaptcha(){// 1.生成验证码字符String text = producer.createText();// 2.生成图片BufferedImage bi = producer.createImage(text);FastByteArrayOutputStream fos = new FastByteArrayOutputStream();try {ImageIO.write(bi, "jpg", fos);// 3.缓存至 redis 中redisCacheUtils.setCacheObject(RedisConstants.PWD_CODE_KEY+ UserHolderUtils.getUserId(),text,RedisConstants.PWD_CODE_TTL);// 4.返回验证码图片的base64编码String imgEncode = Base64.encodeBase64String(fos.toByteArray());fos.flush();return Result.ok(imgEncode);}catch (Exception e){return Result.error();}finally {fos.close();}}
}

5.4 修改密码验证码校验代码

package com.zhulang.waveedu.basic.vo;import com.zhulang.waveedu.common.util.RegexUtils;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;/*** 修改密码时的VO** @author 狐狸半面添* @create 2023-01-29 22:09*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UpdatePwdVO {/*** 第一次输入的密码*/@NotBlank(message = "输入密码为空")@Pattern(regexp = RegexUtils.RegexPatterns.PASSWORD_REGEX, message = "密码格式错误,应为8-16位的数字或字母")private String firPassword;/*** 第二次输入的密码(确认密码)*/@NotBlank(message = "输入密码为空")@Pattern(regexp = RegexUtils.RegexPatterns.PASSWORD_REGEX, message = "密码格式错误,应为8-16位的数字或字母")private String secPassword;/*** 图片验证码字符*/@NotBlank(message = "验证码为空")@Pattern(regexp = RegexUtils.RegexPatterns.CAPTCHA_CODE_REGEX, message = "验证码格式错误")private String code;
}
/*** User登录,注册,推出登录,注销的控制器** @author 狐狸半面添* @create 2023-01-17 23:14*/
@RestController
@RequestMapping("/user")
public class UserController {/*** 修改密码** @param updatePwdVO 两个密码+code* @return 修改情况*/@PutMapping("/updatePwd")public Result updatePwd(@Validated @RequestBody UpdatePwdVO updatePwdVO){return userService.updatePwd(updatePwdVO);}
}
/*** ServiceImpl实现了IService,提供了IService中基础功能的实现* 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现** @author 狐狸半面添* @create 2023-01-17 23:31*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {@Resourceprivate RedisCacheUtils redisCacheUtils;@Overridepublic Result updatePwd(UpdatePwdVO updatePwdVO) {// 1.如果两个密码不一致,则返回errorif (!updatePwdVO.getFirPassword().equals(updatePwdVO.getSecPassword())){return Result.error(HttpStatus.HTTP_VERIFY_FAIL.getCode(), "两次密码不一致");}// 2.校验验证码是否正确String code = redisCacheUtils.getCacheObject(RedisConstants.PWD_CODE_KEY + UserHolderUtils.getUserId());// 2.1 不存在则返回if (code==null){return Result.error(HttpStatus.HTTP_VERIFY_FAIL.getCode(),"验证码已失效,请重新获取");}// 2.2 存在但不一致则清除并返回(忽略大小写)if (!code.equalsIgnoreCase(updatePwdVO.getCode())){redisCacheUtils.deleteObject(RedisConstants.PWD_CODE_KEY + UserHolderUtils.getUserId());return Result.error(HttpStatus.HTTP_VERIFY_FAIL.getCode(),"验证码错误,请重新获取");}// 3.验证码正确,则从缓存中移除redisCacheUtils.deleteObject(RedisConstants.PWD_CODE_KEY + UserHolderUtils.getUserId());// 4.修改密码LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();wrapper.eq(User::getId,UserHolderUtils.getUserId()).set(User::getPassword,PasswordEncoderUtils.encode(updatePwdVO.getFirPassword()));this.update(wrapper);// 5.返回return Result.ok();}
}

5.5 测试图片验证码生成

image-20230130022428405

我们对这段Base64进行解码,这里就使用一个在线工具:base64图片在线转换工具 - 站长工具 (chinaz.com)

📍 注意需要在上图中得到的字符串前面指定转换格式:data:image/jpg;base64,

image-20230130022806425

image-20230130022952203

7.验证码未来发展的讨论

验证码技术其实是一个攻防博弈的动态发展的技术,因此,随着验证码发展得越来越安全,相应的破解技术也跟着不断发展,随之而来的,是双方的
资源成本越来越高。

例如对于互联网应用来说,商业验证码、短信验证码等这些验证码,如果应用的业务流程控制不好,很容易被羊毛党、黑客等人利用,造成成本极大浪费。而网上逐渐出现的各种奇葩验证码,人眼难辨,也让很多正常用户叫苦不迭。

对于破解方来说,随着机器学习以及人工打码等技术的不断发展,可选择的技术手段也越来越多。验证码技术,也在破解方的大量资源投入下,变得越来越鸡肋。并且,很多互联网应用逐渐复杂的验证方式,安全性提高的同时,也提高了很多正常用户的使用门槛,成为了各种电信诈骗的温床。验证码技术逐渐开始偏离了最初的初衷。

而未来的验证码技术,一方面,会通过引入更多的验证元素来进一步提高验证码的安全性,例如刷脸、刷指纹、语音交互、点选你购买过的商品等。另一方面,通过对行为式验证码的研究逐渐深入,可以从传统的面向结果的验证,转化成面向过程的行为验证,并逐渐减少人工参与,降低关键信息被劫持的风险,形成更多对用户无感知的验证方式。例如分析用户鼠标轨迹、按键频率、使用习惯等。

总之,验证码技术,是一个所有人都将亲身参与的精彩世界。

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

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

相关文章

Qt音乐播放器实现(带歌词功能)

最近期末项目要写一个音乐播放器&#xff0c;作为新人头疼了好久&#xff0c;参考了各方文档&#xff0c;总算憋出来了一个&#xff0c;歌词功能用的是一个大佬实现的&#xff0c;然后我就调用了&#xff0c;就是我那个lyricwidget.cpp和头文件就是直接弄过来的&#xff0c;但是…

JavaWeb音乐网站开发:Ajax异步获取歌词文件并显示,以及实现音频与歌词对应同步的方法

在开发音乐网站或APP的时候&#xff0c;都必须要面对这一个问题&#xff1a;如何让正在播放的歌曲的歌词与音频对应&#xff1f;也就是歌曲正在唱哪一句对应显示这一句的歌词&#xff1f;这便是今天这篇博客主要谈论的主题&#xff0c;先抛开爬虫爬取这一说&#xff0c;今天讲的…

Android解析lrc里的歌词

解析记歌词的类。这是解析.lrc文件里的歌词。 解析歌词使用的是输入流&#xff0c;然后吧歌词和歌词时间放在JavaBean的列表里。具体代码如下&#xff1a; package com.zzm.android.Handler;import java.io.BufferedReader; import java.io.File; import java.io.FileInputStr…

iPhone上的lrc播放器可以在播放mp3文件时显示歌词

https://apps.apple.com/cn/app/%E6%96%B0lrc%E6%92%AD%E6%94%BE%E5%99%A82/id1535214306 长久以来&#xff0c;在iPhone上播放lrc字幕一直是一个大的问题。因为苹果自带的音乐播放器不支持lrc歌词字幕&#xff0c;而市面上也少有支持lrc歌词字幕的音乐播放器App。 现在&…

音乐歌词同步实现指南

有道友问了我这个问题&#xff0c;所以在这写了下思路首先&#xff0c;我默认你至少已经了解了h5的audio标签的相关属性&#xff0c;直入正题audio使用指南audio标签audio之HTML 事件属性 old歌词 一般的说&#xff0c;lyric文件由时间的tag标签组成&#xff0c;如截图所示 现在…

把乐谱换成c语言程序的软件,有没有什么软件可以把音乐换成简谱?

一、下载&#xff1a; 1。先把WAV或MP3转换成MIDI 下载地址&#xff1a; http://www.skycn.com/soft/10460.html 2。再把MIDI转成五线谱 下载地址&#xff1a; http://soft.pcnow.com.cn/soft/17237.shtml 二、安装&#xff1a; 1。安装MP3转MIDI工具&#xff0c;一直点下一步即…

安卓文字绘制和歌词器的简单实现

文章目录 安卓绘制文本的细节和歌词动画实战绘制简单文本绘制API绘制线设计多条线的原因 中心绘制x轴居中align居中宽度居中 正中心绘制 动画绘制原理过度绘制解决过度绘制 Demo点赞评论找我要哦 安卓绘制文本的细节和歌词动画实战 绘制文本有许多细节&#xff0c;这篇文章从绘…

歌词同步

歌词同步 前面的话歌词同步的需求歌词加载歌词显示歌曲播放时歌词的滚动计算歌词滚动范围如何同步歌词的滚动细节计算 鼠标拖拽时歌词的滚动自由滚动歌词 源码 前面的话 最近发现我们班同学做了一个很酷的Demo&#xff0c;这个Demo实现了一个很不错的歌词同步&#xff0c;着实…

终于等到你!Guitar Pro 8.1版本简谱功能首发

Guitar Pro是一款非常流行的音乐制谱软件它不仅适用于吉他谱还可以用于其他乐器的制谱。历经5年多时间研发Guitar Pro 在2022年正式发布了全新的8系列版本时隔不到1年Guitar Pro又给广大中国用户带来期盼已久的简谱功能下面让我带领大家一起体验吧 软件souurl.cn/BPln7d 图1 G…

人工智能火爆 国内这几家计算机视觉公司值得关注

&#xfeff;&#xfeff; 人工智能自降生以来就仿佛自带光环、备受瞩目。尤其是今年&#xff0c;国务院印发的《新一代人工智能发展规划》中提出&#xff0c;到2030年使中国人工智能理论、技术与应用总体达到世界领先水平&#xff0c;成为世界主要人工智能创新中心&#xff0c…

国内最值得关注的10家人工智能语音识别公司

在谈人工智能的时候&#xff0c;一定不能不谈语音识别&#xff0c;语音识别是人机交互的入口&#xff0c;是指机器/程序接收、解释声音&#xff0c;或理解和执行口头命令的能力。目前国内外都有公司在语音领域有所投入&#xff0c;其中包括才成立几年的初创企业&#xff0c;也包…

运气好到了一品

今天运气好&#xff0c;连续几把都赢了&#xff0c;一口气从布衣五品冲上了智贤一品&#xff0c;纪念一下。

到了智贤一品

朋友今天运气不错&#xff0c;今天连续打几把&#xff0c;都赢了&#xff0c;所以品级从智贤五品一路升级到了智贤一品&#xff0c;他很开心&#x1f601;。

之伏一品纪念

昨天运气不错&#xff0c;玩了几把&#xff0c;结果巴巴赢&#xff0c;从智贤一品升级到了之伏五品&#xff0c;之伏往升级的需要赢的次数就更多了&#xff0c;我感觉自己没啥希望了&#xff0c;有一个已经非常满足了。因为再过两个月会重新排名&#xff0c;重新从零开始升级。…

每周一品 · 永磁联轴器 Magnetic Couplings

永磁联轴器&#xff08;Magnetic Couplings&#xff09;是通过永磁体的磁力将原动机与工作机联接起来的一种新型联轴器&#xff0c;它无需直接的机械联接&#xff0c;而是利用永磁体之间磁耦合&#xff0c;利用磁场可穿透一定的空间距离和物质材料的特性&#xff0c;进行机械能…

生活随记-斗地主直线一品

今天运气很好&#xff0c;进阶到了直线一品&#xff0c;记录下这个历史时刻吧

java毕业设计闲一品交易平台(附源码、数据库)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven Vue 等等组成&#xff0c;B/…

springboot闲一品交易平台系统(文档+源码)

大家好&#xff0c;我是火旺技术&#xff0c;一个混迹在java圈的码农,今天要和大家聊的 是一款基于springboot的闲一品交易平台系统网站,项目源码请联系火旺&#xff0c;目前有各类成品 毕设 javaweb ssh ssm springboot等等项目框架&#xff0c;源码丰富&#xff0c;欢迎咨询。…

每周一品 · 海尔贝克阵列 Halbach Array

海尔贝克阵列&#xff08;Halbach Array、Halbach permanent magnet&#xff09;是一种磁体结构&#xff0c;1979年美国学者Klaus Halbach在做电子加速实验时发现了这种特殊的永磁体结构并逐步完善&#xff0c;最终形成了“Halbach”磁体&#xff0c;它是工程上近似理想的结构&…