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

SpringBoot 分布式验证码登录方案

前言

为了防止验证系统被暴力破解,很多系统都增加了验证码效验,比较常见的就是图片二维码,业内比较安全的是短信验证码,当然还有一些拼图验证码,加入人工智能的二维码等等,我们今天的主题就是前后端分离的图片二维码登录方案。

前后端未分离的验证码登录方案

传统的项目大都是基于session交互的,前后端都在一个项目里面,比如传统的SSH项目或者一些JSP系统,当前端页面触发到获取验证码请求,可以将验证码里面的信息存在上下文中,所以登录的时候只需要 用户名、密码、验证码即可。

验证码生成流程如下

在这里插入图片描述

登录验证流程如下

在这里插入图片描述
可以发现,整个登录流程还是依赖session上下文的,并且由后端调整页面。

前后端分离的验证码登录方案

随着系统和业务的不停升级,前后端代码放在一起的项目越来越臃肿,已经无法快速迭代和职责区分了,于是纷纷投入了前后端分离的怀抱,发现代码和职责分离以后,开发效率越来越高了,功能迭代还越来越快,但是以前的验证码登录方案就要更改了。

验证码生成流程如下

在这里插入图片描述
对比原来的方案,增加了redis中间件,不再是存在session里面了,但是后面怎么区分这个验证码是这个请求生成的呢?所以我们加入了唯一标识符来区分

登录验证流程如下

在这里插入图片描述可以发现,基于前后端分离的分布式项目登录方案对比原来,加了一个redis中间件和token返回,不再依赖上下文session,并且页面调整也是由后端换到了前端

动手撸轮子

基于验证码的轮子还是挺多的,本文就以Kaptcha这个项目为例,通过springboot项目集成Kaptcha来实现验证码生成和登录方案。

Kaptcha介绍

Kaptcha是一个基于SimpleCaptcha的验证码开源项目
我找的这个轮子是基于SimpleCaptcha二次封装的,maven依赖如下


<!--Kaptcha是一个基于SimpleCaptcha的验证码开源项目-->
<dependency><groupId>com.github.penggle</groupId><artifactId>kaptcha</artifactId><version>2.3.2</version>
</dependency>

新建项目并加入依赖

依赖主要有 SpringBoot、Kaptcha、Redis

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.lzp</groupId><artifactId>kaptcha</artifactId><version>1.0-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.0.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><dependencies><!--Kaptcha是一个基于SimpleCaptcha的验证码开源项目--><dependency><groupId>com.github.penggle</groupId><artifactId>kaptcha</artifactId><version>2.3.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- redis依赖commons-pool 这个依赖一定要添加 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.3</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

Redis配置类RedisConfig

@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory){RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.setConnectionFactory(redisConnectionFactory);return redisTemplate;}}

验证码配置类KaptchaConfig

@Configuration
public class KaptchaConfig {@Beanpublic DefaultKaptcha producer(){DefaultKaptcha defaultKaptcha = new DefaultKaptcha();Properties properties = new Properties();properties.setProperty("kaptcha.border", "no");properties.setProperty("kaptcha.border.color", "105,179,90");properties.setProperty("kaptcha.textproducer.font.color", "black");properties.setProperty("kaptcha.image.width", "110");properties.setProperty("kaptcha.image.height", "40");properties.setProperty("kaptcha.textproducer.char.string","23456789abcdefghkmnpqrstuvwxyzABCDEFGHKMNPRSTUVWXYZ");properties.setProperty("kaptcha.textproducer.font.size", "30");properties.setProperty("kaptcha.textproducer.char.space","3");properties.setProperty("kaptcha.session.key", "code");properties.setProperty("kaptcha.textproducer.char.length", "4");properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
//        properties.setProperty("kaptcha.obscurificator.impl","com.xxx");可以重写实现类properties.setProperty("kaptcha.noise.impl","com.google.code.kaptcha.impl.NoNoise");Config config = new Config(properties);defaultKaptcha.setConfig(config);return defaultKaptcha;}

验证码控制层CaptchaController
为了方便代码写一块了,讲究看

package com.lzp.kaptcha.controller;import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.lzp.kaptcha.service.CaptchaService;
import com.lzp.kaptcha.vo.CaptchaVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import sun.misc.BASE64Encoder;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;@RestController
@RequestMapping("/captcha")
public class CaptchaController {@Autowiredprivate DefaultKaptcha producer;@Autowiredprivate CaptchaService captchaService;@ResponseBody@GetMapping("/get")public CaptchaVO getCaptcha() throws IOException {// 生成文字验证码String content = producer.createText();// 生成图片验证码ByteArrayOutputStream outputStream = null;BufferedImage image = producer.createImage(content);outputStream = new ByteArrayOutputStream();ImageIO.write(image, "jpg", outputStream);// 对字节数组Base64编码BASE64Encoder encoder = new BASE64Encoder();String str = "data:image/jpeg;base64,";String base64Img = str + encoder.encode(outputStream.toByteArray()).replace("\n", "").replace("\r", "");CaptchaVO captchaVO  =captchaService.cacheCaptcha(content);captchaVO.setBase64Img(base64Img);return  captchaVO;}}

验证码返回对象CaptchaVO

package com.lzp.kaptcha.vo;public class CaptchaVO {/*** 验证码标识符*/private String captchaKey;/*** 验证码过期时间*/private Long expire;/*** base64字符串*/private String base64Img;public String getCaptchaKey() {return captchaKey;}public void setCaptchaKey(String captchaKey) {this.captchaKey = captchaKey;}public Long getExpire() {return expire;}public void setExpire(Long expire) {this.expire = expire;}public String getBase64Img() {return base64Img;}public void setBase64Img(String base64Img) {this.base64Img = base64Img;}
}

Redis封装类 RedisUtils
网上随意找的,类里面注明来源,将就用,代码较多就不贴了。
验证码方法层CaptchaService

package com.lzp.kaptcha.service;import com.lzp.kaptcha.utils.RedisUtils;
import com.lzp.kaptcha.vo.CaptchaVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import java.util.UUID;@Service
public class CaptchaService {@Value("${server.session.timeout:300}")private Long timeout;@Autowiredprivate RedisUtils redisUtils;private final String CAPTCHA_KEY = "captcha:verification:";public CaptchaVO cacheCaptcha(String captcha){//生成一个随机标识符String captchaKey = UUID.randomUUID().toString();//缓存验证码并设置过期时间redisUtils.set(CAPTCHA_KEY.concat(captchaKey),captcha,timeout);CaptchaVO captchaVO = new CaptchaVO();captchaVO.setCaptchaKey(captchaKey);captchaVO.setExpire(timeout);return captchaVO;}}

用户登录对象封装LoginDTO

package com.lzp.kaptcha.dto;public class LoginDTO {private String userName;private String pwd;private String captchaKey;private String captcha;public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getPwd() {return pwd;}public void setPwd(String pwd) {this.pwd = pwd;}public String getCaptchaKey() {return captchaKey;}public void setCaptchaKey(String captchaKey) {this.captchaKey = captchaKey;}public String getCaptcha() {return captcha;}public void setCaptcha(String captcha) {this.captcha = captcha;}
}

登录控制层UserController
这块我写逻辑代码了,相信大家都看的懂

package com.lzp.kaptcha.controller;import com.lzp.kaptcha.dto.LoginDTO;
import com.lzp.kaptcha.utils.RedisUtils;
import com.lzp.kaptcha.vo.UserVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate RedisUtils redisUtils;@PostMapping("/login")public UserVO login(@RequestBody LoginDTO loginDTO)  {Object captch = redisUtils.get(loginDTO.getCaptchaKey());if(captch == null){// throw 验证码已过期}if(!loginDTO.getCaptcha().equals(captch)){// throw 验证码错误}// 查询用户信息//判断用户是否存在 不存在抛出用户名密码错误//判断密码是否正确,不正确抛出用户名密码错误//构造返回到前端的用户对象并封装信息和生成tokenreturn new UserVO();}
}

验证码获取和查看

在这里插入图片描述
在这里插入图片描述

相关文章:

SpringBoot 分布式验证码登录方案

前言 为了防止验证系统被暴力破解&#xff0c;很多系统都增加了验证码效验&#xff0c;比较常见的就是图片二维码&#xff0c;业内比较安全的是短信验证码&#xff0c;当然还有一些拼图验证码&#xff0c;加入人工智能的二维码等等&#xff0c;我们今天的主题就是前后端分离的…...

vite.config.js文件配置代理设置VITE_APP_BASE_API

.env.development文件 ENV development # base api VITE_APP_BASE_API /dev-api.env.production文件 ENV production # base api VITE_APP_BASE_API /apidefine: {process.env: {VITE_APP_BASE_API: https://xxx.com}},server: {hmr: true, // vue3 vite配置热更新不用手动…...

优橙内推海南专场——5G网络优化(中高级)工程师

可加入就业QQ群&#xff1a;801549240 联系老师内推简历投递邮箱&#xff1a;hrictyc.com 内推公司1&#xff1a;南京华苏科技有限公司 内推公司2&#xff1a;南京欣网通信股份有限公司 内推公司3&#xff1a;广东华讯工程有限公司 南京华苏科技有限公司 南京华苏科技有…...

5083: 【递推】走方格

题目描述 在平面上有一些二维的点阵。 这些点的编号就像二维数组的编号一样&#xff0c;从上到下依次为第 1 至第 n 行&#xff0c;从左到右依次为第 1 至第 m 列&#xff0c;每一个点可以用行号和列号来表示。 现在有个人站在第 1 行第 1 列&#xff0c;要走到第 n 行第 m …...

多种方式计算当天与另一天的间隔天数 Java实现

这里不会记录纯原生写法&#xff0c;因为现在基本都是被工具类封装好的&#xff0c;所以会记录好用的工具类来简化开发&#xff0c;当然自己可以研究写一个年月日各自做减法的纯原生工具类。 踩坑处(System.currentTimeMillis) 这里指的是使用System.currentTimeMillis()方法。…...

Python基础学习004——for循环与字符串

""" 1.for循环基本语法 2.做指定次数的循环,range()函数 3.continue的使用 4.字符串的定义与使用:转义符,原生字符 5.获取字符串长度,字符串索引的使用 6.切片,翻转字符串 7.字符串的查找find 8.字符串的替换replace 9.字符串的拆分split 10.字符串的链接join &…...

【发展史】鼠标的发展史

最早可以追溯到1952年&#xff0c;皇家加拿大海军将5针保龄球放在能够侦测球面转动的硬件上&#xff0c;这个硬件再将信息转化成光标在屏幕上移动&#xff0c;用作军事计算机输入。这是我们能够追溯到的最早的依靠手部运动进行光标移动的输入设备。但当时这个东西不叫鼠标&…...

ThinkPHP6 多应用模式之验证码模块的配置与验证

Thinphp6 官方的验证码模块的配置是有问题的&#xff0c;或者说需要手工配置。 在配置期间&#xff0c;我尝试了多种&#xff08;包括按照官方文档、路由等&#xff09;方法都验证失败。 存在2个问题&#xff1a; 1、多应用模式下&#xff0c;验证码的配置文件依然读取全局的…...

数据结构笔记——树和图(王道408)(持续更新)

文章目录 传送门前言树&#xff08;重点&#xff09;树的数据结构定义性质 二叉树的数据结构定义性质储存结构 二叉树算法先中后序遍历层次展开法递归模拟法 层次遍历遍历序列逆向构造二叉树 线索二叉树&#xff08;难点&#xff09;定义线索化的本质 二叉树线索化线索二叉树中…...

Redis 主从

目录 ​编辑一、构建主从架构 1、集群结构 2、准备实例和配置 &#xff08;1&#xff09;创建目录 &#xff08;2&#xff09;修改原始配置 &#xff08;3&#xff09;拷贝配置文件到每个实例目录 &#xff08;4&#xff09;修改每个实例的端口&#xff0c;工作目录 &a…...

嵌入式学习笔记(63)位操作实战

(1)给定一个整型数a&#xff0c;设置a的bit3&#xff0c;保证其他位不变。 a | (1<<3) (2)给定一个整形数a&#xff0c;设置a的bit3~bit7&#xff0c;保持其他位不变 a | (0x1f<<3) (3)给定一个整型数a&#xff0c;清除a的bit15&#xff0c;保证其他位不变。 a …...

8位机adc采样正弦波频率

相位/峰峰值高电平&#xff1f; 检 测峰值电压&#xff1f; y 开始计数 检测零电压 y 计数器值16ms/20ms 斩波开x关x延时 tt 频率 1/2t 电路 增减常数 aT...

react中使用监听

在 React 中&#xff0c;您可以使用 addEventListener 函数来监听事件。以下是一个示例&#xff1a; import React, { useRef, useEffect } from react;function App() {const inputRef useRef(null);useEffect(() > {inputRef.current.addEventListener(input, handleInp…...

Java基础总结

0、Java语言 1.java和c 2.编译和解释 3.jre和jdk&#xff0c;jvm 简单来说&#xff0c;编译型语言是指编译器针对特定的操作系统将源代码一次性翻译成可被该平台执行的机器码&#xff1b;解释型语言是指解释器对源程序逐行解释成特定平台的机器码并立即执行。 Java 语言既具…...

基于SSM的OA办公系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…...

【第25例】IPD体系进阶:需求分析团队RAT

目录 简介 RAT CSDN学院相关内容推荐 作者简介 简介 RAT是英文Requirement Analysis Team英文首字母的简称,也即需求分析团队,每个产品线都需要设定对应的一个RAT的组织。 RAT主要负责产品领域内需求的分析活动,是RMT的支撑团队: 这个时候可以将RAT细化为PL-RAT团队,…...

5G与无人驾驶:引领未来交通的新潮流

5G与无人驾驶&#xff1a;引领未来交通的新潮流 随着5G技术的快速发展和普及&#xff0c;无人驾驶技术也日益受到人们的关注。5G技术为无人驾驶提供了更高效、更准确、更及时的通信方式&#xff0c;从而改变了我们对交通出行的认知和使用方式。本文将探讨5G技术在无人驾驶领域的…...

FreeRTOS学习2018.6.27

《FreeRTOS学习》 1.freeRTOS基本功能函数&#xff1a; 定义任务&#xff1a;ATaskFunction(); 创建任务&#xff1a;xTaskCreate(); 改优先级&#xff1a;vTaskPrioritySet(); 系统延时&#xff1a;vTaskDelay(); 精确延时&#xff1a;vTaskDelayUntil(); 空闲任务钩子函数&…...

【异常】理解Java中的异常处理机制

标题&#xff1a;理解Java中的异常处理机制 摘要&#xff1a; 异常处理是Java编程中的重要概念之一&#xff0c;它可以帮助开发者识别和处理程序运行过程中的错误和异常情况。本文将深入探讨Java中的异常处理机制&#xff0c;包括异常的分类、异常处理的语法和最佳实践。通过示…...

很久没写JAVA程序了,原来用GMAIL发送邮件这么简单

写完代码,配置了GMAIL,死活发布出去,碰到了错误535-5.7.8 Username and Password not accepted. 首先先写代码,然后配置GMAIL. 第一写代码: 当你需要在 Spring Boot 中实现邮件通知时,你可以使用 Spring 的 JavaMailSender 来发送电子邮件。首先,确保你的 Spring Boo…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

React19源码系列之 事件插件系统

事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

C++.OpenGL (20/64)混合(Blending)

混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践

作者&#xff1a;吴岐诗&#xff0c;杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言&#xff1a;融合数据湖与数仓的创新之路 在数字金融时代&#xff0c;数据已成为金融机构的核心竞争力。杭银消费金…...

CSS3相关知识点

CSS3相关知识点 CSS3私有前缀私有前缀私有前缀存在的意义常见浏览器的私有前缀 CSS3基本语法CSS3 新增长度单位CSS3 新增颜色设置方式CSS3 新增选择器CSS3 新增盒模型相关属性box-sizing 怪异盒模型resize调整盒子大小box-shadow 盒子阴影opacity 不透明度 CSS3 新增背景属性ba…...