Springboot 手搓 后端 滑块验证码生成

目录

一、效果演示

二、后端滑块验证码生成思路

三、原理解析

四、核心代码拿走


滑块验证码react前端实现,见我的这篇博客:前端 React 弹窗式 滑动验证码实现_react中使用阿里云滑块验证码2.0前端接入及相关视觉-CSDN博客

一、效果演示

生成的案例图片:

视频演示:

滑块验证码演示

二、后端滑块验证码生成思路

1、后端需要生成对应的两个图片(拼图图片和拼图背景图片,图片内存尽量小一点)和对应位置(x和y, 等高拼图只需要记录x即可);

2、验证码生成服务,生成唯一的标识uuid(可考虑雪花算法生成),将生成图片生成后得到的位置信息即x(非登高拼图x和y)记录到缓存中,建议使用redis存储,即使分布式也能使用;

3、将验证码数据返回给前端,格式参考如下:

三、原理解析

要想生成拼图形状的拼图,我们需要运用到一些数学知识,核心代码如下:

通过 圆的标准方程 (x-a)²+(y-b)²=r²,标识圆心(a,b),半径为r的圆,>=的在外侧,<的内侧。

简单来看就是这样的一个模型:

 /*** 随机生成拼图图轮廓数据** @param randomR1 圆点距离随机值* @return 0和1,其中0表示没有颜色,1有颜色*/private static int[][] createTemplateData(int randomR1) {// 拼图轮廓数据int[][] data = new int[puzzleWidth][puzzleHeight];// 拼图去掉凹凸的白色距离int xBlank = puzzleWidth - distance;int yBlank = puzzleHeight - distance;// 记录圆心的位置值int topOrBottomX = puzzleWidth / 2;int leftOrRightY = puzzleHeight / 2;// 凹时对应的位置int topYOrLeftX = distance - randomR1 + radius;int rightX = puzzleWidth - topYOrLeftX;int bottomY = puzzleHeight - topYOrLeftX;// 凸时对应的位置int topYOrLeftXR = distance + randomR1 - radius;int rightXR = puzzleWidth - topYOrLeftXR;int bottomYR = puzzleHeight - topYOrLeftXR;double rPow = Math.pow(radius, 2);/* 随机获取判断条件 */Random random = new Random();Integer[] randomCondition = new Integer[]{random.nextInt(3),random.nextInt(3),random.nextInt(3),random.nextInt(3)};/*计算需要的拼图轮廓(方块和凹凸),用二维数组来表示,二维数组有两张值,0和1,其中0表示没有颜色,1有颜色圆的标准方程 (x-a)²+(y-b)²=r²,标识圆心(a,b),半径为r的圆*/for (int i = 0; i < puzzleWidth; i++) {for (int j = 0; j < puzzleHeight; j++) {/* 凹时对应的圆点 */// 顶部的圆心位置为(puzzleWidth / 2, topYOrLeftX)double top = Math.pow(i - topOrBottomX, 2) + Math.pow(j - topYOrLeftX, 2);// 底部的圆心位置为(puzzleWidth / 2, puzzleHeight - topYOrLeftX)double bottom = Math.pow(i - topOrBottomX, 2) + Math.pow(j - bottomY, 2);// 左侧的圆心位置为(topYOrLeftX, puzzleHeight / 2)double left = Math.pow(i - topYOrLeftX, 2) + Math.pow(j - leftOrRightY, 2);// 右侧的圆心位置为(puzzleWidth - topYOrLeftX, puzzleHeight / 2)double right = Math.pow(i - rightX, 2) + Math.pow(j - leftOrRightY, 2);/* 凸时对应的圆点 */// 顶部的圆心位置为(puzzleWidth / 2, topYOrLeftXR)double topR = Math.pow(i - topOrBottomX, 2) + Math.pow(j - topYOrLeftXR, 2);// 底部的圆心位置为(puzzleWidth / 2, puzzleHeight - topYOrLeftXR)double bottomR = Math.pow(i - topOrBottomX, 2) + Math.pow(j - bottomYR, 2);// 左侧的圆心位置为(topYOrLeftXR, puzzleHeight / 2)double leftR = Math.pow(i - topYOrLeftXR, 2) + Math.pow(j - leftOrRightY, 2);// 右侧的圆心位置为(puzzleWidth - topYOrLeftXR, puzzleHeight / 2)double rightR = Math.pow(i - rightXR, 2) + Math.pow(j - leftOrRightY, 2);/* 随机获取条件 */Boolean[][] conditions = new Boolean[][]{new Boolean[]{(j <= distance && topR >= rPow),(j <= distance || top <= rPow),(j <= distance)},new Boolean[]{(j >= yBlank && bottomR >= rPow),(j >= yBlank || bottom <= rPow),(j >= yBlank)},new Boolean[]{(i <= distance && leftR >= rPow),(i <= distance || left <= rPow),(i <= distance)},new Boolean[]{(i >= xBlank && rightR >= rPow),(i >= xBlank || right <= rPow),(i >= xBlank)}};boolean hide = false;for (int c = 0; c < randomCondition.length; c++) {if (conditions[c][randomCondition[c]]) {hide = true;break;}}if (hide) {// 不显示的像素data[i][j] = 0;} else {data[i][j] = 1;}}}return data;}

绘制好需要截取的数据位置后,再来进行剪切:

 /*** 裁剪拼图** @param bgImg             - 原图规范大小之后的大图* @param puzzleImg         - 小图* @param slideTemplateData - 拼图轮廓数据* @param x                 - 坐标x* @param y                 - 坐标y*/private static void cutByTemplate(BufferedImage bgImg, BufferedImage puzzleImg, int[][] slideTemplateData, int x, int y) {int[][] matrix = new int[3][3];int[] values = new int[9];// 虚假的x坐标int fakeX = getRandomFakeX(x);// 创建shape区域,即原图抠图区域模糊和抠出小图/*遍历小图轮廓数据,创建shape区域。即原图抠图处模糊和抠出小图*/for (int i = 0; i < puzzleImg.getWidth(); i++) {for (int j = 0; j < puzzleImg.getHeight(); j++) {// 获取大图中对应位置变色int rgb_ori = bgImg.getRGB(x + i, y + j);// 0和1,其中0表示没有颜色,1有颜色int rgb = slideTemplateData[i][j];if (rgb == 1) {// 设置小图中对应位置变色puzzleImg.setRGB(i, j, rgb_ori);// 大图抠图区域高斯模糊readPixel(bgImg, x + i, y + j, values);fillMatrix(matrix, values);bgImg.setRGB(x + i, y + j, avgMatrix(matrix));// 抠虚假图readPixel(bgImg, fakeX + i, y + j, values);fillMatrix(matrix, values);bgImg.setRGB(fakeX + i, y + j, avgMatrix(matrix));} else {// 这里把背景设为透明puzzleImg.setRGB(i, j, rgb_ori & 0x00ffffff);}}}}

四、核心代码拿走

SliderCaptchaUtil:

package com.xloda.common.tool.captcha.util;import com.xloda.common.tool.captcha.constant.SliderCaptchaConfig;
import com.xloda.common.tool.captcha.pojo.SliderCaptcha;import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;/*** @author Dragon Wu* @since 2025/04/23 10:52* 滑块验证码生成器*/public class SliderCaptchaUtil implements SliderCaptchaConfig {/*** 生成滑块验证码** @param bgImg     1. 传入随机背景图* @param accordant 是否生成登高拼图* @return SliderCaptcha 验证码结果* @throws IOException IO异常*/public static SliderCaptcha generateCaptcha(BufferedImage bgImg, boolean accordant) throws IOException {// 2. 随机生成离左上角的(X,Y)坐标,上限为 [width-puzzleWidth, height-puzzleHeight]。最好离大图左边远一点,上限不要紧挨着大图边界Random random = new Random();// X范围:[puzzleWidth, width - puzzleWidth)int x = random.nextInt(width - 2 * puzzleWidth) + puzzleWidth;// Y范围:[puzzleHeight, height - puzzleHeight)int y = random.nextInt(height - 2 * puzzleHeight) + puzzleHeight;// 3. 创建拼图图像BufferedImage puzzleImg = new BufferedImage(puzzleWidth, puzzleHeight, BufferedImage.TYPE_4BYTE_ABGR);// 4. 随机获取位置数据int randomR1 = getRandomR1();// 5. 随机生成拼图轮廓数据int[][] slideTemplateData = createTemplateData(randomR1);// 6. 从大图中裁剪拼图。抠原图,裁剪拼图cutByTemplate(bgImg, puzzleImg, slideTemplateData, x, y);// 7. 给拼图加边框puzzleImg = ImageUtil.addBorderWithOutline(puzzleImg, borderSize, Color.white);// 8. 判断是否为登高拼图if (accordant) {puzzleImg = reshapeAccordant(puzzleImg, y);return new SliderCaptcha(ImageUtil.toBase64(bgImg),ImageUtil.toBase64(puzzleImg), x);}// 非登高拼图,记录x和yreturn new SliderCaptcha(ImageUtil.toBase64(bgImg),ImageUtil.toBase64(puzzleImg), x, y);}// 随机获取小圆距离点private static int getRandomR1() {Integer[] r1List = new Integer[]{radius * 3 / 2,radius,radius / 2,};int index = new Random().nextInt(r1List.length);return r1List[index];}/*** 随机生成拼图图轮廓数据** @param randomR1 圆点距离随机值* @return 0和1,其中0表示没有颜色,1有颜色*/private static int[][] createTemplateData(int randomR1) {// 拼图轮廓数据int[][] data = new int[puzzleWidth][puzzleHeight];// 拼图去掉凹凸的白色距离int xBlank = puzzleWidth - distance;int yBlank = puzzleHeight - distance;// 记录圆心的位置值int topOrBottomX = puzzleWidth / 2;int leftOrRightY = puzzleHeight / 2;// 凹时对应的位置int topYOrLeftX = distance - randomR1 + radius;int rightX = puzzleWidth - topYOrLeftX;int bottomY = puzzleHeight - topYOrLeftX;// 凸时对应的位置int topYOrLeftXR = distance + randomR1 - radius;int rightXR = puzzleWidth - topYOrLeftXR;int bottomYR = puzzleHeight - topYOrLeftXR;double rPow = Math.pow(radius, 2);/* 随机获取判断条件 */Random random = new Random();Integer[] randomCondition = new Integer[]{random.nextInt(3),random.nextInt(3),random.nextInt(3),random.nextInt(3)};/*计算需要的拼图轮廓(方块和凹凸),用二维数组来表示,二维数组有两张值,0和1,其中0表示没有颜色,1有颜色圆的标准方程 (x-a)²+(y-b)²=r²,标识圆心(a,b),半径为r的圆*/for (int i = 0; i < puzzleWidth; i++) {for (int j = 0; j < puzzleHeight; j++) {/* 凹时对应的圆点 */// 顶部的圆心位置为(puzzleWidth / 2, topYOrLeftX)double top = Math.pow(i - topOrBottomX, 2) + Math.pow(j - topYOrLeftX, 2);// 底部的圆心位置为(puzzleWidth / 2, puzzleHeight - topYOrLeftX)double bottom = Math.pow(i - topOrBottomX, 2) + Math.pow(j - bottomY, 2);// 左侧的圆心位置为(topYOrLeftX, puzzleHeight / 2)double left = Math.pow(i - topYOrLeftX, 2) + Math.pow(j - leftOrRightY, 2);// 右侧的圆心位置为(puzzleWidth - topYOrLeftX, puzzleHeight / 2)double right = Math.pow(i - rightX, 2) + Math.pow(j - leftOrRightY, 2);/* 凸时对应的圆点 */// 顶部的圆心位置为(puzzleWidth / 2, topYOrLeftXR)double topR = Math.pow(i - topOrBottomX, 2) + Math.pow(j - topYOrLeftXR, 2);// 底部的圆心位置为(puzzleWidth / 2, puzzleHeight - topYOrLeftXR)double bottomR = Math.pow(i - topOrBottomX, 2) + Math.pow(j - bottomYR, 2);// 左侧的圆心位置为(topYOrLeftXR, puzzleHeight / 2)double leftR = Math.pow(i - topYOrLeftXR, 2) + Math.pow(j - leftOrRightY, 2);// 右侧的圆心位置为(puzzleWidth - topYOrLeftXR, puzzleHeight / 2)double rightR = Math.pow(i - rightXR, 2) + Math.pow(j - leftOrRightY, 2);/* 随机获取条件 */Boolean[][] conditions = new Boolean[][]{new Boolean[]{(j <= distance && topR >= rPow),(j <= distance || top <= rPow),(j <= distance)},new Boolean[]{(j >= yBlank && bottomR >= rPow),(j >= yBlank || bottom <= rPow),(j >= yBlank)},new Boolean[]{(i <= distance && leftR >= rPow),(i <= distance || left <= rPow),(i <= distance)},new Boolean[]{(i >= xBlank && rightR >= rPow),(i >= xBlank || right <= rPow),(i >= xBlank)}};boolean hide = false;for (int c = 0; c < randomCondition.length; c++) {if (conditions[c][randomCondition[c]]) {hide = true;break;}}if (hide) {// 不显示的像素data[i][j] = 0;} else {data[i][j] = 1;}}}return data;}/*** 裁剪拼图** @param bgImg             - 原图规范大小之后的大图* @param puzzleImg         - 小图* @param slideTemplateData - 拼图轮廓数据* @param x                 - 坐标x* @param y                 - 坐标y*/private static void cutByTemplate(BufferedImage bgImg, BufferedImage puzzleImg, int[][] slideTemplateData, int x, int y) {int[][] matrix = new int[3][3];int[] values = new int[9];// 虚假的x坐标int fakeX = getRandomFakeX(x);// 创建shape区域,即原图抠图区域模糊和抠出小图/*遍历小图轮廓数据,创建shape区域。即原图抠图处模糊和抠出小图*/for (int i = 0; i < puzzleImg.getWidth(); i++) {for (int j = 0; j < puzzleImg.getHeight(); j++) {// 获取大图中对应位置变色int rgb_ori = bgImg.getRGB(x + i, y + j);// 0和1,其中0表示没有颜色,1有颜色int rgb = slideTemplateData[i][j];if (rgb == 1) {// 设置小图中对应位置变色puzzleImg.setRGB(i, j, rgb_ori);// 大图抠图区域高斯模糊readPixel(bgImg, x + i, y + j, values);fillMatrix(matrix, values);bgImg.setRGB(x + i, y + j, avgMatrix(matrix));// 抠虚假图readPixel(bgImg, fakeX + i, y + j, values);fillMatrix(matrix, values);bgImg.setRGB(fakeX + i, y + j, avgMatrix(matrix));} else {// 这里把背景设为透明puzzleImg.setRGB(i, j, rgb_ori & 0x00ffffff);}}}}/*** 随机获取虚假x坐标的值** @param x 真正的x坐标* @return fakeX*/private static int getRandomFakeX(int x) {int puzzleRealWidth = puzzleWidth + 2 * borderSize + 2;Random random = new Random();int fakeX = random.nextInt(width - 2 * puzzleRealWidth) + puzzleRealWidth;if (Math.abs(fakeX - x) <= puzzleRealWidth) {fakeX = width - x;}return fakeX;}/*** 通过拼图图片生成登高拼图图片** @param puzzleImg 拼图图片* @param offsetY   随机生成的y* @return 登高拼图图片*/private static BufferedImage reshapeAccordant(BufferedImage puzzleImg, int offsetY) {BufferedImage puzzleBlankImg = new BufferedImage(puzzleWidth + 2 * borderSize + 2, height, BufferedImage.TYPE_4BYTE_ABGR);Graphics2D graphicsPuzzle = puzzleBlankImg.createGraphics();graphicsPuzzle.drawImage(puzzleImg, 1, offsetY, null);graphicsPuzzle.dispose();return puzzleBlankImg;}private static void readPixel(BufferedImage img, int x, int y, int[] pixels) {int xStart = x - 1;int yStart = y - 1;int current = 0;for (int i = xStart; i < 3 + xStart; i++) {for (int j = yStart; j < 3 + yStart; j++) {int tx = i;if (tx < 0) {tx = -tx;} else if (tx >= img.getWidth()) {tx = x;}int ty = j;if (ty < 0) {ty = -ty;} else if (ty >= img.getHeight()) {ty = y;}pixels[current++] = img.getRGB(tx, ty);}}}private static int avgMatrix(int[][] matrix) {int r = 0;int g = 0;int b = 0;for (int[] x : matrix) {for (int j = 0; j < x.length; j++) {if (j == 1) {continue;}Color c = new Color(x[j]);r += c.getRed();g += c.getGreen();b += c.getBlue();}}return new Color(r / 8, g / 8, b / 8).getRGB();}private static void fillMatrix(int[][] matrix, int[] values) {int filled = 0;for (int[] x : matrix) {for (int j = 0; j < x.length; j++) {x[j] = values[filled++];}}}
}

RandomUtil:

package com.xloda.common.tool.captcha.util;import com.xloda.common.tool.captcha.constant.CaptchaConstants;import java.util.Random;/*** @author Dragon Wu* @since 2025/04/23 18:07* 随机生成器工具*/public class RandomUtil {// 随机获取背景图路径public static String randomBgImgPath() {int index = new Random().nextInt(CaptchaConstants.BG_IMAGES.length);return CaptchaConstants.BG_IMAGES[index];}
}

ImageUtil:

package com.xloda.common.tool.captcha.util;import com.xloda.common.tool.captcha.constant.CaptchaConstants;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;/*** @author Dragon Wu* @since 2025/04/25 10:18* 图片处理工具*/public class ImageUtil {public static BufferedImage addBorderWithOutline(BufferedImage image, int borderWidth, Color borderColor) {// 创建新图像,尺寸扩大以容纳边框BufferedImage result = new BufferedImage(image.getWidth() + borderWidth * 2,image.getHeight() + borderWidth * 2,BufferedImage.TYPE_INT_ARGB);Graphics2D g2d = result.createGraphics();g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);// 获取图像的非透明区域Area area = new Area();for (int y = 0; y < image.getHeight(); y++) {for (int x = 0; x < image.getWidth(); x++) {if ((image.getRGB(x, y) >> 24) != 0x00) {area.add(new Area(new Rectangle(x, y, 1, 1)));}}}// 绘制边框g2d.setColor(borderColor);g2d.setStroke(new BasicStroke(borderWidth * 2));g2d.translate(borderWidth, borderWidth);g2d.draw(area);// 绘制原始图像g2d.drawImage(image, 0, 0, null);g2d.dispose();return result;}// 图片转Base64public static String toBase64(BufferedImage image) throws IOException {// 创建一个字节数组输出流ByteArrayOutputStream os = new ByteArrayOutputStream();// 将BufferedImage写入到输出流中,这里指定图片格式为"png"或"jpg"等ImageIO.write(image, CaptchaConstants.IMG_FORMAT, os);// 将输出流的字节数组转换为Base64编码的字符串String imageBase64 = Base64.getEncoder().encodeToString(os.toByteArray());// 关闭输出流os.close();return CaptchaConstants.BASE64_PREFIX + imageBase64;}
}

SliderCaptcha:

package com.xloda.common.tool.captcha.pojo;import lombok.*;/*** @author Dragon Wu* @since 2025/04/23 10:49* 滑块验证码*/
@AllArgsConstructor
@Getter
@ToString
public class SliderCaptcha {// 验证码背景图private String bgImg;// 验证码滑块private String puzzleImg;// 验证码正确的x位置(此值需自行存入缓存,用于验证码判断)private int x;// 等高拼图时,返回0(非登高拼图,此值需自行存入缓存,用于验证码判断)private int y;public SliderCaptcha(String bgImg, String puzzleImg, int x) {this.bgImg = bgImg;this.puzzleImg = puzzleImg;this.x = x;}
}

CaptchaConstants:

package com.xloda.common.tool.captcha.constant;/*** @author Dragon Wu* @since 2025/04/23 10:53*/
public interface CaptchaConstants {// 图片格式String IMG_FORMAT = "png";// base64前缀String BASE64_PREFIX = "data:image/" + IMG_FORMAT + ";base64,";// 图片存储的目录String FOLDER = "/static/img/captcha/";// 背景图列表(引入依赖后,记得在项目资源目录的该路径下添加对应图片)String[] BG_IMAGES = new String[]{FOLDER + "bg01.png",FOLDER + "bg01.png"};
}

SliderCaptchaConfig

package com.xloda.common.tool.captcha.constant;/*** @author Dragon Wu* @since 2025/04/23 11:09* 滑块验证码的配置*/public interface SliderCaptchaConfig {// 大图宽度(原图裁剪拼图后的背景图)int width = 280;// 大图高度int height = 173;// 小图宽度(滑块拼图),前端拼图的实际宽度:puzzleWidth + 2 * borderSize + 2int puzzleWidth = 66;// 小图高度,前端拼图的实际高度:puzzleHeight + 2 * borderSize + 2int puzzleHeight = 66;// 边框厚度int borderSize = 1;// 小圆半径,即拼图上的凹凸轮廓半径int radius = 8;// 图片一周预留的距离,randomR1最大值不能超过radius * 3 / 2int distance = radius * 3 / 2;
}

接下来继续手搓旋转验证码前后端。

本节,总结到此,学点数学挺有用的!

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

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

相关文章

关于flink两阶段提交高并发下程序卡住问题

先抛出代码 package com.dpf.flink;import com.dpf.flink.sink.MysqlSink; import org.apache.flink.api.common.serialization.SimpleStringSchema; import org.apache.flink.api.common.typeinfo.Types; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.…

html css js网页制作成品——HTML+CSS+js美甲店网页设计(5页)附源码

美甲店 目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&a…

LeetCode[347]前K个高频元素

思路&#xff1a; 使用小顶堆&#xff0c;最小的元素都出去了&#xff0c;省的就是大&#xff0c;高频的元素了&#xff0c;所以要维护一个小顶堆&#xff0c;使用map存元素高频变化&#xff0c;map存堆里&#xff0c;然后输出堆的东西就行了 代码&#xff1a; class Solution…

2024年网站开发语言选择指南:PHP/Java/Node.js/Python如何选型?

2024年网站开发语言选择指南&#xff1a;PHP/Java/Node.js/Python如何选型&#xff1f; 一、8大主流Web开发语言技术对比 1. PHP开发&#xff1a;中小型网站的首选方案 最新版本&#xff1a;PHP 8.3&#xff08;2023年11月发布&#xff09;核心优势&#xff1a; 全球78%的网站…

从数据结构说起(一)

1 揭开数据结构神奇的面纱 1.1 初识数据结构 在C的标准库模板&#xff08;Standard Template Library,STL&#xff09;课程上&#xff0c;我初次结识了《数据结构》。C语言提供的标准库模板是面向对象程序设计与泛型程序设计思想相结合的典范。所谓的泛型编程就是编写不依赖于具…

JAVA--- 关键字static

之前我们学习了JAVA 面向对象的一些基本知识&#xff0c;今天来进阶一下&#xff01;&#xff01;&#xff01; static关键字 static表示静态&#xff0c;是JAVA中的一个修饰符&#xff0c;可以修饰成员方法&#xff0c;成员变量&#xff0c;可用于修饰类的成员&#xff08;变…

4.27比赛总结

文章目录 T1T2法一&#xff1a;倍增求 LCA法二&#xff1a;Dijkstra 求最短路法三&#xff1a;dfs 求深度 T3T4总结 T1 一道非常简单的题&#xff0c;结果我因为一句话没写挂了 80pts…… 题目中没写 a a a 数组要按照 b b b 数组的顺序&#xff0c;所以对于最大方案&#x…

数据一致性巡检总结:基于分桶采样的设计与实现

数据一致性巡检总结&#xff1a;基于分桶采样的设计与实现 背景 在分布式系统中&#xff0c;缓存&#xff08;如 Redis&#xff09;与数据库&#xff08;如 MySQL&#xff09;之间的数据一致性问题是一个常见的挑战。由于缓存的引入&#xff0c;数据在缓存和数据库之间可能存…

SpringBoot与Druid整合,实现主从数据库同步

通过引入主从数据库同步系统&#xff0c;可以显著提升平台的性能和稳定性&#xff0c;同时保证数据的一致性和安全性。Druid连接池也提供了强大的监控和安全防护功能&#xff0c;使得整个系统更加健壮和可靠。 我们为什么选择Druid&#xff1f; 高效的连接管理&#xff1a;Dru…

在Linux系统中安装MySQL,二进制包版

1、检查是否已安装数据库&#xff08;rpm软件包管理器&#xff09; rpm -qa | grep mysql rpm -qa | grep mariadb #centOS7自带mariadb与mysql数据库冲突2、删除已有数据库 rpm -e –nodeps 软件名称 3、官网下载MySQL包 4、上传 # 使用FinalShell或Xshell工具上传&#…

【含文档+PPT+源码】基于SpringBoot电脑DIY装机教程网站的设计与实现

项目介绍 本课程演示的是一款 基于SpringBoot电脑DIY装机教程网站的设计与实现&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套…

Spring Boot 缓存机制:从原理到实践

文章目录 一、引言二、Spring Boot 缓存机制原理2.1 缓存抽象层2.2 缓存注解2.3 缓存管理器 三、入门使用3.1 引入依赖3.2 配置缓存3.3 启用缓存3.4 使用缓存注解3.5 实体类 四、踩坑记录4.1 缓存键生成问题4.2 缓存过期与更新问题4.3 事务与缓存的一致性问题 五、心得体会5.1 …

Spark读取Apollo配置

--conf spark.driver.extraJavaOptions-Dapp.idapollo的app.id -Denvfat -Dapollo.clusterfat -Dfat_metaapollo的meta地址 --conf spark.executor.extraJavaOptions-Dapp.idapollo的app.id -Denvfat -Dapollo.clusterfat -Dfat_metaapollo的meta地址 在spark的提交命令中&…

[逆向工程]如何理解小端序?逆向工程中的字节序陷阱与实战解析

[逆向工程]如何理解小端序&#xff1f;逆向工程中的字节序陷阱与实战解析 关键词&#xff1a;逆向工程、小端序、字节序、二进制分析、数据解析 引言&#xff1a;为什么字节序是逆向工程师的必修课&#xff1f; 在逆向工程中&#xff0c;分析二进制数据是最基础的任务之一。…

项目三 - 任务2:创建笔记本电脑类(一爹多叔)

在本次实战中&#xff0c;我们通过Java的单根继承和多接口实现特性&#xff0c;设计了一个笔记本电脑类。首先创建了Computer抽象类&#xff0c;提供计算的抽象方法&#xff0c;模拟电脑的基本功能。接着定义了NetCard和USB两个接口&#xff0c;分别包含连接网络和USB设备的抽象…

ElasticSearch深入解析(六):集群核心配置

1.开发模式和生产模式 Elasticsearch默认运行在开发模式下&#xff0c;此模式允许节点在配置存在错误时照常启动&#xff0c;仅将警告信息写入日志文件。而生产模式则更为严格&#xff0c;一旦检测到配置错误&#xff0c;节点将无法启动&#xff0c;这是一种保障系统稳定性的安…

【Prometheus-MySQL Exporter安装配置指南,开机自启】

目录 1. 创建 MySQL 监控用户2. 配置 MySQL 认证文件3. 安装 mysqld_exporter4. 配置 Systemd 服务5. 启动并验证服务6. 修改Prometheus配置常见错误排查错误现象排查步骤 6. 验证监控数据关键注意事项 7. Grafana看板 1. 创建 MySQL 监控用户 mysql -uroot -p123456 # 登录M…

redis未授权访问漏洞学习

一、Redis常见用途 1. Redis介绍 全称与起源: Redis全称Remote Dictionary Service(远程字典服务)&#xff0c;最初由antirez在2009年开发&#xff0c;用于解决网站访问记录统计的性能问题。发展历程: 从最初仅支持列表功能的内存数据库&#xff0c;经过十余年发展已支持多种…

4.27搭建用户界面

更新 router下面的index.js添加新的children 先区分一下views文件夹下的不同vue文件&#xff1a; Home.vue是绘制home页面的所有的表格。 Main.vue是架构头部和左侧目录的框架的。 研究一下这个routes对象&#xff0c;就可以发现重定向redirect的奥妙所在&#xff0c;我们先把…

【MySQL】(8) 联合查询

一、联合查询的作用 由于范式的规则&#xff0c;数据分到多个表中&#xff0c;想要查询完整的信息&#xff0c;就需要联合查询多张表。比如查询学生的学生信息和所在班级的信息&#xff0c;就需要联合查询学生表和班级表。 二、联合查询过程 案例&#xff1a;查询学生姓名为孙…