登录验证码高扩展性设计方案

登录验证码高扩展性建设方案

本文分享了一种登录验证码高扩展性的建设方案,通过工厂模式+策略模式,增强了验证码服务中验证码生成器、验证码存储器、验证码图片生成器的扩展性,实现了服务组件的多样化,降低了维护成本

  • 登录验证码高扩展性建设方案
  • 1、前言
  • 2、接口规范
  • 3、流程规范
  • 4、验证码的实际开发示范
  • 5、总结

1、前言

在进行登录时,无论是账号密码登录,还是第三方登录,总是需要输入验证码。

使用验证码可以进行人机判断,提高安全性,过滤恶性攻击。

验证码一般由一串随机字符组成,当然也存在多种生成方式。

验证码的展现方式有很多种,例如图片,语音,手机验证码。

验证码的存储方式有很多种,例如数据库、缓存、JVM本地内存。

验证码的存储又涉及到另一个问题,验证码唯一id如何生成,在分布式项目中,一般可以通过数据库自增序列、缓存自增序列、UUID、雪花算法等方式生成唯一id。

既然生成一个验证码有这么多种方法,那如何设计一种高扩展性的验证码服务架构,使得各个部分的组件可以自由替换呢?这就是本文需要解决的问题。

技术栈:SpringBoot,MySQL,Redis,zookeeper。

设计模式:策略模式。

2、接口规范

根据上述分析,我们需要分别实现验证码的生成、展示、id生成、存储的接口,一般验证码服务需要对外提供生成验证码和校验验证码的api,我们写在一个接口中,四个组件以内部接口类的形式进行开发。


/*** @version 1.0* @description 验证码接口* @date 2023/9/29 15:59*/
public interface CheckCodeService {// 生成验证码public CheckCodeResultDto generate(CheckCodeParamsDto checkCodeParamsDto);// 校验验证码public boolean verify(String key, String code);// 验证码生成器public interface CheckCodeGenerator{String generate(int length);}// 唯一id生成public interface KeyGenerator{String generate(String prefix);}// 验证码存储器public interface CheckCodeStore{void set(String key, String value, Integer expire);String get(String key);void remove(String key);}// 展示包装器public interface DisplayGenerate{String display(String code);}
}

3、流程规范

定义好接口规范后,我们需要对流程进行规范,正常流程是使用验证码生成器生成一个随机的验证码,使用id生成器生成一个唯一id,使用存储器存储验证码和id,以不同的展示方式包装验证码并返回。

此时我们还没有实际开发各个生成器模块,只是对流程进行规范,让不同的展示方法代码实现我们这个规范,所以这个应该通过抽象类的方式实现,各个生成器模块以注入的方式得到。


/*** @version 1.0* @description 验证码接口* @date 2023/9/29 15:59*/
public abstract class AbstractCheckCodeService implements CheckCodeService {// 验证码生成器protected CheckCodeGenerator checkCodeGenerator;// id生成器protected KeyGenerator keyGenerator;// 验证码存储器protected CheckCodeStore checkCodeStore;// 展示包装器protected DisplayGenerate displayGenerator;public abstract void  setCheckCodeGenerator(CheckCodeGenerator checkCodeGenerator);public abstract void  setKeyGenerator(KeyGenerator keyGenerator);public abstract void  setCheckCodeStore(CheckCodeStore CheckCodeStore);public abstract void  setDisplayGenerate(DisplayGenerate displayGenerate);// 生成验证码public GenerateResult generate(CheckCodeParamsDto checkCodeParamsDto,Integer code_length,String keyPrefix,Integer expire){//生成验证码String code = checkCodeGenerator.generate(code_length);//生成唯一idString key = keyGenerator.generate(keyPrefix);//存储验证码checkCodeStore.set(key,code,expire);//验证码展示方法String display = displayGenerator.display(code);//返回验证码生成结果GenerateResult generateResult = new GenerateResult();generateResult.setKey(key);generateResult.setCode(code);generateResult.setDisplay(display);return generateResult;}@Dataprotected class GenerateResult{String key;String code;String display;}public abstract CheckCodeResultDto generate(CheckCodeParamsDto checkCodeParamsDto);// 校验验证码public boolean verify(String key, String code){if (StringUtils.isBlank(key) || StringUtils.isBlank(code)){return false;}String code_l = checkCodeStore.get(key);if (code_l == null){return false;}boolean result = code_l.equalsIgnoreCase(code);if(result){//删除验证码checkCodeStore.remove(key);}return result;}
}

参数类


public class CheckCodeParamsDto {// 验证码展示类型private String checkCodeType;
}

4、验证码的实际开发示范

实际实现抽象类的方法需要完成四个组件的注入即可。


@Service("CheckCodeService")
public class CheckCodeServiceImpl extends AbstractCheckCodeService implements CheckCodeService {@Resource(name="NumberLetterCheckCodeGenerator")@Overridepublic void setCheckCodeGenerator(CheckCodeGenerator checkCodeGenerator) {this.checkCodeGenerator = checkCodeGenerator;}@Resource(name="UUIDKeyGenerator")@Overridepublic void setKeyGenerator(KeyGenerator keyGenerator) {this.keyGenerator = keyGenerator;}@Resource(name="MemoryCheckCodeStore")@Overridepublic void setCheckCodeStore(CheckCodeStore checkCodeStore) {this.checkCodeStore = checkCodeStore;}@Resource(name="PicCheckCodeGenerator")@Overridepublic void setDisplayGenerate(DisplayGenerator displayGenerator) {this.displayGenerator = displayGenerator;}@Overridepublic CheckCodeResultDto generate(CheckCodeParamsDto checkCodeParamsDto) {GenerateResult generate = generate(checkCodeParamsDto, 4, "checkcode:", 60);String key = generate.getKey();String code = generate.getCode();String display = generate.getDisplay();CheckCodeResultDto checkCodeResultDto = new CheckCodeResultDto();checkCodeResultDto.setDisplay(display);checkCodeResultDto.setKey(key);return checkCodeResultDto;}}

涉及的实体类


/*** @version 1.0* @description 验证码生成结果类* @date 2023/9/29 15:48*/
@Data
public class CheckCodeResultDto {// 验证码idprivate String key;// 展示方式private String display;
}

四个组件的实现示范:

验证码生成器:


@Component("NumberLetterCheckCodeGenerator")
public class NumberLetterCheckCodeGenerator implements CheckCodeService.CheckCodeGenerator {@Overridepublic String generate(int length) {String str="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";Random random=new Random();StringBuffer sb=new StringBuffer();for(int i=0;i<length;i++){int number=random.nextInt(36);sb.append(str.charAt(number));}return sb.toString();}
}

唯一id生成器(以UUID的方式生成)


@Component("UUIDKeyGenerator")
public class UUIDKeyGenerator implements CheckCodeService.KeyGenerator {@Overridepublic String generate(String prefix) {String uuid = UUID.randomUUID().toString();return prefix + uuid.replaceAll("-", "");}
}

验证码存储器(以Redis为例)


@Component("MemoryCheckCodeStore")
public class MemoryCheckCodeStore implements CheckCodeService.CheckCodeStore {@Autowiredpublic RedissonClient redissonClient;@Overridepublic void set(String key, String value, Integer expire) {if (get(key)!=null) log.error("codes key conflict");redisTemplate.opsForValue().set(key, value, 300 + new Random().nextInt(100), TimeUnit.SECONDS);}@Overridepublic String get(String key) {return (String) redisTemplate.opsForValue().get(key);}@Overridepublic void remove(String key) {boolean b = redisTemplate.opsForValue().getOperations().delete(key);if (!b) log.info("remove codes key failure");}
}

展示生成器(以图片为例)


@Component("PicCheckCodeGenerate")
public class PicCheckCodeGenerate implements CheckCodeService.DisplayGenerate {@Autowiredprivate DefaultKaptcha kaptcha;@Overrideprivate String display(String code) {// 生成图片验证码ByteArrayOutputStream outputStream = null;BufferedImage image = kaptcha.createImage(code);outputStream = new ByteArrayOutputStream();String imgBase64Encoder = null;try {// 对字节数组Base64编码BASE64Encoder base64Encoder = new BASE64Encoder();ImageIO.write(image, "png", outputStream);imgBase64Encoder = "data:image/png;base64," + EncryptUtil.encodeBase64(outputStream.toByteArray());} catch (IOException e) {e.printStackTrace();} finally {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}return imgBase64Encoder;}
}

5、总结

本文针对验证码生成过程节点多、实现方案多等特点,提出了一种高扩展性的建设方案,并给出了实际开发示例。

本文提出的验证码生成框架具有高扩展性,可以对任一组件进行创建开发、动态替换。如果结合Nacos和yaml,还可以实现组件热更新。

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

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

相关文章

8617 阶乘数字和

这是一个关于计算阶乘结果所有位上的数字之和的问题。我们可以通过以下步骤来解决这个问题&#xff1a; 1. 首先&#xff0c;我们需要一个函数来计算阶乘。由于n的范围可以达到50&#xff0c;阶乘的结果可能非常大&#xff0c;所以我们需要使用一个可以处理大整数的数据类型&a…

adb shell logcat -b all|grep如何可以grep两个子串?

在adb shell logcat命令中结合grep来过滤日志时&#xff0c;如果你想要同时匹配两个子串&#xff0c;你可以使用管道&#xff08;|&#xff09;将两个grep命令连接起来&#xff0c;或者使用grep的-E&#xff08;或egrep&#xff0c;它等同于-E&#xff09;选项来支持扩展的正则…

[课程][原创]opencv图像在C#与C++之间交互传递

opencv图像在C#与C之间交互传递 课程地址&#xff1a;https://edu.csdn.net/course/detail/39689 无限期视频有效期 课程介绍课程目录讨论留言 你将收获 学会如何封装C的DLL 学会如何用C#调用C的DLL 掌握opencv在C#和C传递思路 学会如何配置C的opencv 适用人群 拥有C#…

报错:pathspec ‘xxx‘ did not match any file(s) known to git

在 escode 中进行分支切换时报如下错误 PS > git checkout xxx error: pathspec xxx did not match any file(s) known to git远程分支已经在 gitlab 客户端手动创建&#xff0c;在 escode 中也使用了拉取之类的操作&#xff0c;但是切换分支时依然报错。 解决方案 查看分…

怎么找到DNS服务器的地址?

所有域都注册到域名名称服务器&#xff08;DNS&#xff09;点&#xff0c;以解析域名应指向的IP地址。此查找类似于在查找个人名称并查找其电话号码时的电话簿如何运行。如果DNS服务器设置错误或指向错误的名称服务器&#xff0c;则域可能无法加载相应的网页。 如何查找当前的…

【深度学习】C++ onnx Yolov8 目标检测推理

【深度学习】C onnx Yolov8 目标检测推理 导出onnx模型代码onnx_detect_infer.honnx_detect_infer.cppmain.cppCMAKELIST 导出onnx模型 python 中导出 from ultralytics import YOLO# Load the YOLOv8 model model YOLO("best.pt")# # Export the model to ONNX f…

解决多个QGroupBox在布局中,当layoutspacing=0时,结合Line消除边框过粗的干扰。

QGroupBox {border: 0px solid gray;border-top: 1px solid rgb(208, 208, 208); margin-top: 0.5em; } QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top center; padding: 0 3px; background-color: white; } 设置Line color: rgb(208, 208,…

(3)Java 8 实战第二版——使用流和Lambda进行高效编程

集合工厂 List<String> friends Arrays.asList("Raphael", "Olivia"); friends.set(0, "Richard"); friends.add("Thibaut"); ←---- 抛出一个UnsupportedModificationException异常通过工厂方法创建的Collection的底层…

jvm参数模板

一、8G以下jvm参数模板 垃圾回收器选择cms 1、关键参数 MetaspaceSize,MaxMetaspaceSize 大约是20.8MB。这个默认值是JVM内部设定的&#xff0c;过小的元空间导致oom DisableExplicitGC 可用于禁止应用程序通过调用 System.gc() 方法来显式触发垃圾回 cms参数四剑客 -X…

CrossViT:用于图像分类的交叉注意多尺度Vision Transformer

提出了一种双支路Transformer来组合不同大小的图像补丁(即变压器中的令牌)以产生更强的图像特征。方法处理具有不同计算复杂度的两个独立分支的小补丁和大补丁令牌,然后这些令牌纯粹通过注意多次融合以相互补充。此外,为了减少计算量,开发了一个简单而有效的基于交叉关注的令…

C++基础编程100题-020 OpenJudge-1.3-20 计算2的幂

更多资源请关注纽扣编程微信公众号 http://noi.openjudge.cn/ch0103/20/ 描述 给定非负整数n&#xff0c;求2n。 输入 一个整数n。0 < n < 31。 输出 一个整数&#xff0c;即2的n次方。 样例输入 3样例输出 8参考程序-1 #include<bits/stdc.h> using nam…

JavaScript高级程序设计(第四版)--学习记录之对象、类和面向对象编程(中)

创建对象方式 工厂模式&#xff1a;用于抽象创建特定对象的过程。可以解决创建多个类似对象的问题&#xff0c;但没有解决对象标识问题。&#xff08;即新创建的对象是什么类型&#xff09; function createPerson(name, age, job) { let o new Object(); o.name name; o.age…

Android:移动垃圾软件

讲解政策相关,最近升级AI扫荡系统和证书防高风险,回复按留言时间来排,请耐心等待 移动垃圾软件 官方政策公告行为透明、信息披露清晰保护用户数据不要损害移动体验软件准则反垃圾软件政策Google API 服务用户数据政策官方政策公告 ​ 在 Google,我们相信,如果我们关注用户…

oracle创建用户和赋权

在 Oracle 数据库中&#xff0c;要创建一个用户并授予权限&#xff0c;可以按照以下步骤进行操作&#xff1a;步骤一&#xff1a;创建用户 sql CREATE USER yonghuming IDENTIFIED BY 123456; 这里将 yonghuming 替换为要创建的用户名&#xff0c;123456 替换为用户的密码。步…

Retrofit源码阅读

动态代理在 Android 中的应用&#xff1a;Retrofit 源码解析 在之前的文章 《Andriod 网络框架 OkHttp 源码解析》 中我们分析了 OkHttp 的源代码。现在我们就来分析一下 OkHttp 的兄弟框架 Retrofit。关于 Retrofit 的注解的使用&#xff0c;可以参考其官方文档&#xff1a;h…

控制台厂商配额查询

概述 厂商推送限制 每个厂商通道都有对应的厂商配额和 QPS 限制&#xff0c;当请求超过限制且已配置厂商回执时&#xff0c;MobPush会采取以下措施&#xff1a; 当开发者推送请求超过厂商配额时&#xff0c;MobPush将通过自有通道进行消息下发。当开发者推送请求超过厂商 QPS…

java default注解怎么设置数组默认值

在Java中&#xff0c;为注解的数组类型属性设置默认值时&#xff0c;需要使用大括号{}包围数组元素。如果数组默认值只有一个元素&#xff0c;也可以直接使用该元素而不需要大括号。下面是一个示例&#xff0c;展示了如何为注解的数组类型属性设置默认值&#xff1a; import j…

Spark on k8s 源码解析执行流程

Spark on k8s 源码解析执行流程 1.通过spark-submit脚本提交spark程序 在spark-submit脚本里面执行了SparkSubmit类的main方法 2.运行SparkSubmit类的main方法&#xff0c;解析spark参数&#xff0c;调用submit方法 3.在submit方法里调用doRunMain方法&#xff0c;最终调用r…

Activity top resumed state loss timeout for ActivityRecord 报错原因是?

Activity top resumed state loss timeout for ActivityRecord 这个错误通常不是直接显示给用户看的&#xff0c;而是Android系统内部日志&#xff08;如Logcat&#xff09;中的一个错误信息&#xff0c;它指示了系统在尝试恢复或管理某个Activity的状态时遇到了问题。这个错误…

算法-位图与底层运算逻辑

文章目录 1. 位图的理论基础2. 完整版位图实现3. 底层的运算逻辑-位运算 1. 位图的理论基础 首先我们要理解什么是位图, 位图的一些作用是什么 位图法就是bitmap的缩写。所谓bitmap&#xff0c;就是用每一位来存放某种状态&#xff0c;适用于大规模数据&#xff0c;但数据状态又…