Java 图片验证码需求分析

在这里插入图片描述

💗wei_shuo的个人主页

💫wei_shuo的学习社区

🌐Hello World !


图片验证码

需求分析

在这里插入图片描述

  • 连续因输错密码而登录失败时,记录其连续输错密码的累加次数;若在次数小于5时,用户输入正确的密码并成功登录,则次数被清零
  • 连续5次因输错密码而登录失败后,系统弹框提示【您已连续5次输入错误的密码,暂时不允许登录,请10分钟后再次尝试登录】;点击提示框中的【确定】按钮,提示框被关闭
  • 10分钟内再次尝试登录,则系统弹框提示【您已连续5次输入错误的密码,暂时不允许登录,7分43秒后可再次尝试登录】;点击提示框中的【确定】按钮,提示框被关闭;注:提示框中的剩余时间动态倒数至0分0秒
  • 10分钟后,用户可再次尝试登录;此时,若用户在输错密码次数小于5次时成功登录,则其连续输错密码的次数、曾被锁定1次的信息被清空归零;反之,若用户再次连续5次输错密码,则系统弹框提示【您已连续10次输入错误的密码,账号已被锁定、不允许登录,请联系管理员解锁】;点击提示框中的【确定】按钮,提示框被关闭。此后,用户每次用该账号尝试登录时,均弹出此提示框。此时,在运营端,该用户详情页面中的【登录状态】已被自动切换为【锁定】。用户须主动联系莫族密运营人员,运营人员确认用户没有被盗号、遭遇网络攻击等风险后,主动将其【登录状态】置为【解锁】;此时,用户连续输错密码的次数、曾被锁定2次的信息被清空归零
  • 用户登录时,须输入正确的【验证码】
  • 若用户看不清,则可点击【看不清?换一张】字样,也可直接点击验证码部件,点击后自动刷新验证码
  • 点击【登录】按钮后,【用户名】、【密码】、【验证码】这3项但凡有1项校验不通过,则登录失败,【用户名】、【密码】、【验证码】框中已录入的内容被清空,验证码自动刷新
  • 点击【登录】按钮后,若【用户名】、【密码】校验通过,唯独【验证码】校验不通过,则登录失败的系统提示内容为【验证码错误,请重新录入验证码】。同时验证码自动刷新。
  • 【验证码】的有效时间为60秒,超过之后则失效,但不自动刷新。失效之后若录入正确的【用户名】【密码】同时录入页面上已失效的【验证码】,则登录失败,且登录失败的系统提示内容为【验证码错误,请重新录入验证码】,同时验证码自动刷新

实施

验证码接口 | 请求头方式传递
  • 依赖导入
        <!-- 添加图形验证码依赖 --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-captcha</artifactId><version>5.8.5</version></dependency>
  • 图片验证码接口编写
    /*** 生成验证码图片* @return*/@ApiOperation("获取图形验证码")@GetMapping("/identifyImage")public Result<String> identifyImage(HttpServletResponse response,@ApiParam(value = "图形验证码id,无值:生成验证码,有值:刷新验证码")@RequestParam(name = "codeId", required = false) String codeId) throws IOException {// 创建验证码,设置宽、高、长度、干扰线数量LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 90, 4, 100);// 获取验证码字符串,赋值codeString code = lineCaptcha.getCode();if (codeId == null) {// IdWorker.getId():IdWorker工具类生成唯一ID,并转换成String类型codeId = String.valueOf(IdWorker.getId());// 将codeId、code.toUpperCase()、过期时间60秒:存储入Redis中// code.toUpperCase():code装换成大写形式存储redisOps.set(codeId,code.toUpperCase(),60);} else {redisOps.set(codeId,code.toUpperCase(),60);}// 将图片验证码codeId设置请求头中response.setHeader("codeId", codeId);// 获取向客户端发送响应数据的输出流try (ServletOutputStream outputStream = response.getOutputStream()) {// 验证码图片数据写入到输出流lineCaptcha.write(outputStream);} catch (Exception e) {throw new AuthException("图形验证码输出错误");}return Result.succ(codeId);}
  • Postman调用测试
http://localhost:9036/api/identifyImage

在这里插入图片描述

在这里插入图片描述

验证码接口 | base64方式传递
    /*** 生成验证码图片* @return*/@ApiOperation("获取图形验证码")@GetMapping("/identifyImage")public Result<IdentifyImageResp> identifyImage(HttpServletResponse response,@ApiParam(value = "图形验证码id,无值:生成验证码,有值:刷新验证码")@RequestParam(name = "codeId", required = false) String codeId) throws IOException {LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 90, 4, 100);String code = lineCaptcha.getCode();if (codeId == null) {codeId = String.valueOf(IdWorker.getId());redisOps.set(codeId,code.toUpperCase(),60);} else {redisOps.set(codeId,code.toUpperCase(),60);}IdentifyImageResp identifyImageResp = new IdentifyImageResp(codeId, lineCaptcha.getImageBase64Data());return Result.succ(identifyImageResp);}

在这里插入图片描述

登录接口
  • 登录接口编写
@PostMapping("login")
@ApiOperation("用户登录")
public Result login(@Validated @RequestBody LoginRequest request) {// request.getCodeId():请求体中获取codeId// redisOps.get(request.getCodeId():codeId为键,获取redis中对应的值String codeId = (String) redisOps.get(request.getCodeId());if (codeId.isEmpty()){throw new AuthException("验证码已过期请刷新重试");}AuthContext login = authService.login(request);// 登录成功后,通过 login.getMerchant() 获取到登录的用户对象,跟新登录信息Merchant merchant = login.getMerchant();merchant.setLastLoginAt(merchant.getLoginAt());merchant.setLoginAt(new Date());merchant.setLastLoginIp(merchant.getLoginIp());merchant.setLoginIp(CommonTools.getIp(httpServletRequest));merchantRepo.updateById(merchant);login.setMerchant(merchant);// JsonMapper.objectToJson(login):将login对象转换成 JSON 格式的字符串log.info("LOGIN - > {}", JsonMapper.objectToJson(login));return Result.succ("登录成功");
}
  • LoginRequest.java:请求体字段
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginRequest {@NotEmpty@ApiModelProperty("登录名")private String username;@NotEmpty@ApiModelProperty("密码,md5加密全小写")private String password;@ApiModelProperty("验证码")private String code;@ApiModelProperty("验证码Id")private String codeId;}
  • AuthService.java
    public AuthContext login(LoginRequest login) {// 登录验证和处理if (StringUtils.isBlank(login.getUsername())) {throw new AuthException("用户名不能为空");}if (StringUtils.isBlank(login.getPassword())) {throw new AuthException("密码不能为空");}// 缓存清空,登出操作logout();Merchant merchant = findMerchantByLoginEmail(login.getUsername());if (merchant == null) {authError("账户不存在,或状态不正确");} else if (merchant.getIsLocked()) {authError("账户已停用");}// 从redis获取login.getUsername()+"lock-time")的键对应的值if (redisOps.get(login.getUsername()+"lock-time") != null){// redisOps.getExpire:获取 Redis 中指定键的过期时间long expire = redisOps.getExpire(login.getUsername() + "lock-time");// 转换为分钟int minutes = (int) (expire / 60);// 转换为秒钟int seconds = (int) (expire % 60);authError("您已连续5次输入错误的密码,暂时不允许登录,"+minutes+"分"+seconds+"秒后可再次尝试登录");}System.out.println(merchant.getLoginPassword());System.out.println(SecretUtils.encrypt(login.getPassword()));Integer errorNum = (Integer) redisOps.get(login.getUsername());if (!merchant.getLoginPassword().equals(SecretUtils.encrypt(login.getPassword()))) {//密码错误次数为null时创建键值对if (errorNum == null){redisOps.set(login.getUsername(),1);}else if  ((errorNum > 0 && errorNum < 4) || (errorNum > 5 && errorNum < 10)){//密码错误次数为0-4、5-10时incrredisOps.incr(login.getUsername(),1);}else if (errorNum+1==5){//密码错误次数为5时锁定10分钟redisOps.set(login.getUsername()+"lock-time","lock",600);authError("您已连续5次输入错误的密码,暂时不允许登录,请10分钟后再次尝试登录");}else {//密码错误次数为10时锁定merchant.setIsLocked(true);merchantRepo.updateById(merchant);authError("您已连续10次输入错误的密码,账号已被锁定、不允许登录,请联系管理员解锁");}authError("密码不正确");}String code= (String) redisOps.get(login.getCodeId());if (code == null || login.getCode()==null || !code.equals(login.getCode().toUpperCase())){authError("请输入正确的验证码");}
//        merchant.setLoginPassword("*");String token = Sha.sha256(UUID.randomUUID().toString());AuthContext authContext = new AuthContext(token, merchant, null);redisOps.set(token, JsonMapper.objectToJson(authContext), authProp.getExpiresSeconds());CookieUtils.setCookie(response, "/", authProp.getTokenHeader(), token, authProp.getExpiresSeconds());//登陆完成删除账号错误次数if (errorNum!=null)redisOps.delete(login.getUsername());return authContext;}public String logout() {String cookie = CookieUtils.getCookie(request, authProp.getTokenHeader());String token = StringUtils.isNotBlank(cookie) ? cookie : request.getHeader(authProp.getTokenHeader());if (StringUtils.isNotBlank(token)) {redisOps.delete(token);}return "登出成功";}private void authError(String errorMsg) {throw new AuthException(errorMsg);}
  • Postman测试

在这里插入图片描述


🌼 结语:创作不易,如果觉得博主的文章赏心悦目,还请——点赞👍收藏⭐️评论📝


在这里插入图片描述

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

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

相关文章

微服务负载均衡器Ribbon

1.什么是Ribbon 目前主流的负载方案分为以下两种&#xff1a; 集中式负载均衡&#xff0c;在消费者和服务提供方中间使用独立的代理方式进行负载&#xff0c;有硬件的&#xff08;比如 F5&#xff09;&#xff0c;也有软件的&#xff08;比如 Nginx&#xff09;。 客户端根据…

使用Kibana让es集群形象起来

部署Elasticsearch集群详细步骤参考本人&#xff1a; https://blog.csdn.net/m0_59933574/article/details/134605073?spm1001.2014.3001.5502https://blog.csdn.net/m0_59933574/article/details/134605073?spm1001.2014.3001.5502 kibana部署 es集群设备 安装软件主机名…

MQ-7一氧化碳传感器模块功能实现(STM32)

认识MQ-7模块与其工作原理 首先来认识MQ-7模块&#xff0c;MQ-7可以检测空气中的一氧化碳&#xff08;CO&#xff09;浓度。他采用半导体气敏元件来检测CO的气体浓度&#xff0c;其灵敏度高、反应速度快、响应时间短、成本低廉等特点使得它被广泛应用于智能家居、工业自动化、环…

minio客户端基本操作

minio客户端基本操作 桶 创建桶 如果要创建新的桶 输入名称&#xff0c;点击创建即可&#xff0c;默认权限就行 删除桶 点击要删除的桶 点击删除 修改桶 如果哪天需要修改桶的权限或者其他信息&#xff0c;还是先点击这个桶进入详情 然后点击要修改的属性&#xff0c;选择…

Qt5.15.2静态编译 VS2017 with static OpenSSL

几年前编译过一次Qt静态库:VS2015编译Qt5.7.0生成支持XP的静态库,再次编译,毫无压力。 一.环境 系统:Windows 10 专业版 64位 编译器:visual studio 2017 第三方工具:perl,ruby和python python用最新的3.x.x版本也是可以的 这三个工具都需要添加到环境变量,安装时勾选…

JavaScript 的初步学习上篇

JavaScript 的介绍 JavaScript 之父 布兰登 . 艾奇 (Brendan Eich) ,1995 年, 用 10 天时间完成 JavaScript 的设计. JavaScript 和 Java 的关系 两者之间就像老婆和老婆饼的关系,即毫无关系, JavaScript 最初的名字叫LiveScript,为了蹭 Java 热度,才改名为 JavaScript.JavaScr…

【2023 云栖】阿里云田奇铣:大模型驱动 DataWorks 数据开发治理平台智能化升级

云布道师 本文根据 2023 云栖大会演讲实录整理而成&#xff0c;演讲信息如下&#xff1a; 演讲人&#xff1a;田奇铣 | 阿里云 DataWorks 产品负责人 演讲主题&#xff1a;大模型驱动 DataWorks 数据开发治理平台智能化升级 随着大模型掀起 AI 技术革新浪潮&#xff0c;大数…

C#中openFileDialog控件的使用方法

目录 一、OpenFileDialog基本属性 二、使用 OpenFile 从筛选的选择中打开文件 1.示例源码 2.生成效果 3. 其它示例 三、使用 StreamReader 以流的形式读取文件 1.示例源码 2.生成效果 四、一种新颖的Windows窗体应用文件设计方法 在C#中&#xff0c;OpenFileDialog控件…

AIGC,ChatGPT AI绘画 Midjourney 注册流程详细步骤

AI 绘画,Midjourney完成高清图片绘制,轻松掌握AI工具。 前期准备: ① 一个能使用的谷歌账号 ② 可以访问外网 Midjourney注册 1.进入midjourney官网https://www.midjourney.com 点击左下角”Join the Beta”,就可以注册,第一次使用的小伙伴会弹出提示,只需要点击Acc…

C语言程序设计知识点总结归纳(全书)

C知识点总结归纳目录 第一章 程序设计和C语言一、C的入门小概念二、程序设计的问题三、首先要搞清楚编译器、编辑器和IDE的区别 第二章 算法——程序的灵魂一、程序算法数据结构二、算法的特性三、怎样表示一个算法四、结构化程序的设计方法 第三章 C程序设计——顺序程序设计一…

vue3+ts 依赖注入 provide inject

父级&#xff1a; <template><div><h1>App.vue (爷爷级别)</h1><label><input type"radio" v-model"colorVal" value"red" name"color" />红色</label><label><input type"r…

【STM32】GPIO输出

1 GPIO简介 &#xff08;1&#xff09;GPIO&#xff08;General Purpose Input Output&#xff09;通用输入输出口 &#xff08;2&#xff09;可配置为8种输入输出模式 &#xff08;3&#xff09;引脚电平&#xff1a;0V~3.3V&#xff0c;部分引脚可容忍5V&#xff08;可以输…

因式分解的几何意义

本来准备和女儿一起玩一道几何题&#xff0c;想想还是算了&#xff0c;不如讲点更有趣的。 任何因式分解都是在堆积木&#xff0c;不信你看&#xff1a; 二项式定理&#xff0c;洋灰三角&#xff0c;都是面积&#xff0c;体积&#xff0c;超维体积的拼接&#xff0c;一个大超…

Linux学习笔记之六(进程之间的管道通信和信号处理)

目录 1、管道通信1.1、无名管道1.1、有名管道 2、信号处理2.1、信号的种类和发送2.2、信号的接受和处理 1、管道通信 管道通信是一个设备中进程与进程之间通信的一种方式&#xff0c;分为无名管道和有名管道两种。前者只能用于有亲缘关系的进程之间的通信&#xff0c;如父子进…

RC-MVSNet:无监督的多视角立体视觉与神经渲染--论文笔记(2022年)

RC-MVSNet&#xff1a;无监督的多视角立体视觉与神经渲染--论文笔记&#xff08;2022年&#xff09; 摘要1 引言2 相关工作2.1 基于监督的MVS2.2 无监督和自监督MVS2.3 多视图神经渲染 3 实现方法3.1 无监督的MVS网络 Chang, D. et al. (2022). RC-MVSNet: Unsupervised Multi-…

leetcode面试经典150题——32 串联所有单词的子串(中等+困难)

题目&#xff1a; 串联所有单词的子串(1中等) 描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串&#xff08;包括相同的字符串&…

Linux 命令vim(编辑器)

(一)vim编辑器的介绍 vim是文件编辑器&#xff0c;是vi的升级版本&#xff0c;兼容vi的所有指令&#xff0c;同时做了优化和延伸。vim有多种模式&#xff0c;其中常用的模式有命令模式、插入模式、末行模式&#xff1a;。 (二)vim编辑器基本操作 1 进入vim编辑文件 1 vim …

百度智能云正式上线Python SDK版本并全面开源

文章目录 前言一、SDK的优势二、千帆SDK&#xff1a;快速落地LLM应用三、如何快速上手千帆SDK3.1、SDK快速启动3.2. SDK进阶指引 3.3. 通过Langchain接入千帆SDK4、开源社区 前言 百度智能云千帆大模型平台再次升级&#xff01;在原有API基础上&#xff0c;百度智能云正式上线…

单链表原来是这样实现的!

文章目录 前言1. 链表的概念及结构1.1在链表里&#xff0c;每节“车厢”是什么样的呢&#xff1f;1.2为什么还需要指针变量来保存下⼀个节点的位置&#xff1f; 2. 单链表的实现1. 定义结构体(Seqlist)2. 打印函数(SLTPrint)小插曲&#xff0c;创建节点函数CreateNode3. 尾插函…

Qt 串口编程-从入门到实战

1. Qt 串口通信流程解析 1.1 串行通信和并行通信对比 并行通信适合距离较短的通信&#xff0c;且信号容易受干扰&#xff0c;成本高串口通讯-设备&#xff08;蓝牙&#xff0c; wifi&#xff0c; gprs&#xff0c; gps&#xff09; 1.2 Qt 串口通信具体流程 1. 创建 QSerial…