RSA加密与签名的区别
文章目录
- 一、签名验签原理
- 二 RSAUtils 工具类
- 三、通过x509Certificate来获取CA证书的基本信息
- 四、 通过公钥获取公钥长度
一、签名验签原理
签名的本质其实就是加密,但是由于签名无需还原成明文,因此可以在加密前进行哈希处理。所以签名其实就是哈希+加密,而验签就是哈希+解密+比较。
签名过程:对明文做哈希,拼接头信息,用私钥进行加密,得到签名。
验签过程:用公钥解密签名,然后去除头信息,对明文做哈希,比较2段哈希值是否相同,相同则验签成功。
代码编写: Cipher 用于加密 Signature 用于签名
二 RSAUtils 工具类
package com.example.tr34.application.utils;import android.util.Base64;import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;import javax.crypto.Cipher;/*** date:2020/9/4 0004* author:wsm (Administrator)* funcation: 私钥有问题,base64解码失败*/public class RSAUtils {private static String RSA = "RSA";/*** *** RSA最大加密大小*/private final static int MAX_ENCRYPT_BLOCK = 117;/*** *** RSA最大解密大小*/private final static int MAX_DECRYPT_BLOCK = 128;/*** 随机生成RSA密钥对(默认密钥长度为1024)** @return*/public static KeyPair generateRSAKeyPair() {return generateRSAKeyPair(1024);}/*** 随机生成RSA密钥对** @param keyLength 密钥长度,范围:512~2048<br>* 一般1024* @return*/public static KeyPair generateRSAKeyPair(int keyLength) {try {KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA);kpg.initialize(keyLength);return kpg.genKeyPair();} catch (NoSuchAlgorithmException e) {e.printStackTrace();return null;}}/*** 用公钥加密 <br>* 每次加密的字节数,不能超过密钥的长度值减去11** @param data 需加密数据的byte数据* @param publicKey 公钥* @return 加密后的byte型数据*/public static byte[] encryptData(byte[] data, PublicKey publicKey) {try {Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");// 编码前设定编码方式及密钥cipher.init(Cipher.ENCRYPT_MODE, publicKey);// 传入编码数据并返回编码结果return cipher.doFinal(data);} catch (Exception e) {e.printStackTrace();return null;}}/*** 用私钥解密** @param encryptedData 经过encryptedData()加密返回的byte数据* @param privateKey 私钥* @return*/public static byte[] decryptData(byte[] encryptedData, PrivateKey privateKey) {try {Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");cipher.init(Cipher.DECRYPT_MODE, privateKey);return cipher.doFinal(encryptedData);} catch (Exception e) {return null;}}/*** 通过公钥byte[](publicKey.getEncoded())将公钥还原,适用于RSA算法** @param keyBytes* @return* @throws NoSuchAlgorithmException* @throws InvalidKeySpecException*/public static PublicKey getPublicKey(byte[] keyBytes) throws NoSuchAlgorithmException,InvalidKeySpecException {X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(RSA);PublicKey publicKey = keyFactory.generatePublic(keySpec);return publicKey;}/*** 通过私钥byte[]将公钥还原,适用于RSA算法** @param keyBytes* @return* @throws NoSuchAlgorithmException* @throws InvalidKeySpecException*/public static PrivateKey getPrivateKey(byte[] keyBytes) throws NoSuchAlgorithmException,InvalidKeySpecException {PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(RSA);PrivateKey privateKey = keyFactory.generatePrivate(keySpec);return privateKey;}/*** 使用N、e值还原公钥** @param modulus* @param publicExponent* @return* @throws NoSuchAlgorithmException* @throws InvalidKeySpecException*/public static PublicKey getPublicKey(String modulus, String publicExponent)throws NoSuchAlgorithmException, InvalidKeySpecException {BigInteger bigIntModulus = new BigInteger(modulus);BigInteger bigIntPrivateExponent = new BigInteger(publicExponent);RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigIntModulus, bigIntPrivateExponent);KeyFactory keyFactory = KeyFactory.getInstance(RSA);PublicKey publicKey = keyFactory.generatePublic(keySpec);return publicKey;}/*** 使用N、d值还原私钥** @param modulus* @param privateExponent* @return* @throws NoSuchAlgorithmException* @throws InvalidKeySpecException*/public static PrivateKey getPrivateKey(String modulus, String privateExponent)throws NoSuchAlgorithmException, InvalidKeySpecException {BigInteger bigIntModulus = new BigInteger(modulus);BigInteger bigIntPrivateExponent = new BigInteger(privateExponent);RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigIntModulus, bigIntPrivateExponent);KeyFactory keyFactory = KeyFactory.getInstance(RSA);PrivateKey privateKey = keyFactory.generatePrivate(keySpec);return privateKey;}/*** 从字符串中加载公钥** @param publicKeyStr 公钥数据字符串* @throws Exception 加载公钥时产生的异常*/public static PublicKey loadPublicKey(String publicKeyStr) throws Exception {try {byte[] buffer = Base64.decode(publicKeyStr,Base64.DEFAULT);KeyFactory keyFactory = KeyFactory.getInstance(RSA);X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);return (RSAPublicKey) keyFactory.generatePublic(keySpec);} catch (NoSuchAlgorithmException e) {throw new Exception("无此算法");} catch (InvalidKeySpecException e) {throw new Exception("公钥非法");} catch (NullPointerException e) {throw new Exception("公钥数据为空");}}/*** 从字符串中加载私钥<br>* 加载时使用的是PKCS8EncodedKeySpec(PKCS#8编码的Key指令)。** @param privateKeyStr* @return* @throws Exception*/public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception {try {byte[] buffer = Base64.decode(privateKeyStr,Base64.DEFAULT);// X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);KeyFactory keyFactory = KeyFactory.getInstance(RSA);return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);} catch (NoSuchAlgorithmException e) {throw new Exception("无此算法");} catch (InvalidKeySpecException e) {throw new Exception("私钥非法");} catch (NullPointerException e) {throw new Exception("私钥数据为空");}}/*** 用公钥分段加密 <br>* 每次加密的字节数,不能超过密钥的长度值减去11** @param data 需加密数据的byte数据* @param publicKey 公钥* @return 加密后的byte型数据*/public static byte[] PublicSubData(byte[] data, PublicKey publicKey) {try {Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");// 编码前设定编码方式及密钥cipher.init(Cipher.ENCRYPT_MODE, publicKey);// 传入编码数据并返回编码结果
// return cipher.doFinal(data);int inputLen = data.length;ByteArrayOutputStream out = new ByteArrayOutputStream();int offSet = 0;byte[] cache;int i = 0;// 对数据分段加密while (inputLen - offSet > 0){if (inputLen - offSet > MAX_ENCRYPT_BLOCK){cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);} else{cache = cipher.doFinal(data, offSet, inputLen - offSet);}out.write(cache, 0, cache.length);i++;offSet = i * MAX_ENCRYPT_BLOCK;}byte[] decryptedData = out.toByteArray();out.close();return decryptedData;} catch (Exception e) {e.printStackTrace();return null;}}/*** 用私钥分段解密** @param encryptedData 经过encryptedData()加密返回的byte数据* @param privateKey 私钥* @return*/public static byte[] PrivateSubData(byte[] encryptedData, PrivateKey privateKey) {try {Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");cipher.init(Cipher.DECRYPT_MODE, privateKey);
// return cipher.doFinal(encryptedData);// 返回UTF-8编码的解密信息int inputLen = encryptedData.length;ByteArrayOutputStream out = new ByteArrayOutputStream();int offSet = 0;byte[] cache;int i = 0;// 对数据分段解密while (inputLen - offSet > 0){if (inputLen - offSet > MAX_DECRYPT_BLOCK){cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);} else{cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);}out.write(cache, 0, cache.length);i++;offSet = i * MAX_DECRYPT_BLOCK;}byte[] decryptedData = out.toByteArray();out.close();return decryptedData;} catch (Exception e) {return null;}}/*** 从文件中输入流中加载公钥** @param in 公钥输入流* @throws Exception 加载公钥时产生的异常*/public static PublicKey loadPublicKey(InputStream in) throws Exception {try {return loadPublicKey(readKey(in));} catch (IOException e) {throw new Exception("公钥数据流读取错误");} catch (NullPointerException e) {throw new Exception("公钥输入流为空");}}/*** 从文件中加载私钥** @param in 私钥文件名* @return 是否成功* @throws Exception*/public static PrivateKey loadPrivateKey(InputStream in) throws Exception {try {return loadPrivateKey(readKey(in));} catch (IOException e) {throw new Exception("私钥数据读取错误");} catch (NullPointerException e) {throw new Exception("私钥输入流为空");}}/*** 读取密钥信息** @param in* @return* @throws IOException*/private static String readKey(InputStream in) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(in));String readLine = null;StringBuilder sb = new StringBuilder();while ((readLine = br.readLine()) != null) {if (readLine.charAt(0) == '-') {continue;} else {sb.append(readLine);sb.append('\r');}}return sb.toString();}/*** 打印公钥信息** @param publicKey*/public static void printPublicKeyInfo(PublicKey publicKey) {RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;System.out.println("----------RSAPublicKey----------");System.out.println("Modulus.length=" + rsaPublicKey.getModulus().bitLength());System.out.println("Modulus=" + rsaPublicKey.getModulus().toString());System.out.println("PublicExponent.length=" + rsaPublicKey.getPublicExponent().bitLength());System.out.println("PublicExponent=" + rsaPublicKey.getPublicExponent().toString());}public static void printPrivateKeyInfo(PrivateKey privateKey) {RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;System.out.println("----------RSAPrivateKey ----------");System.out.println("Modulus.length=" + rsaPrivateKey.getModulus().bitLength());System.out.println("Modulus=" + rsaPrivateKey.getModulus().toString());System.out.println("PrivateExponent.length=" + rsaPrivateKey.getPrivateExponent().bitLength());System.out.println("PrivatecExponent=" + rsaPrivateKey.getPrivateExponent().toString());}
}
三、通过x509Certificate来获取CA证书的基本信息
//创建X509工厂类CertificateFactory cf = CertificateFactory.getInstance("X.509");//创建证书对象X509Certificate oCert = (X509Certificate)cf.generateCertificate(inStream);// inStream证书的传入数据inStream.close();SimpleDateFormat dateformat = new SimpleDateFormat("yyyy/MM/dd");String info = null;//获得证书版本info = String.valueOf(oCert.getVersion());System.out.println("证书版本:"+info);//获得证书序列号info = oCert.getSerialNumber().toString(16);System.out.println("证书序列号:"+info);//获得证书有效期Date beforedate = oCert.getNotBefore();info = dateformat.format(beforedate);System.out.println("证书生效日期:"+info);Date afterdate = oCert.getNotAfter();info = dateformat.format(afterdate);System.out.println("证书失效日期:"+info);//获得证书主体信息info = oCert.getSubjectDN().getName();System.out.println("证书拥有者:"+info);//获得证书颁发者信息info = oCert.getIssuerDN().getName();System.out.println("证书颁发者:"+info);//获得证书签名算法名称info = oCert.getSigAlgName();System.out.println("证书签名算法:"+info);byte[] byt = oCert.getExtensionValue("1.2.86.11.7.9");String strExt = new String(byt);System.out.println("证书扩展域:" + strExt);byt = oCert.getExtensionValue("1.2.86.11.7.1.8");String strExt2 = new String(byt);System.out.println("证书扩展域2:" + strExt2);
四、 通过公钥获取公钥长度
通过X509Certificate.getPublicKey 获取公钥
/*** Gets the key length of supported keys* @param pk PublicKey used to derive the keysize* @return -1 if key is unsupported, otherwise a number >= 0. 0 usually means the length can not be calculated,* for example if the key is an EC key and the "implicitlyCA" encoding is used.*/public static int getKeyLength(final PublicKey pk) {int len = -1;if (pk instanceof RSAPublicKey) {final RSAPublicKey rsapub = (RSAPublicKey) pk;len = rsapub.getModulus().bitLength();} else if (pk instanceof JCEECPublicKey) {final JCEECPublicKey ecpriv = (JCEECPublicKey) pk;final org.bouncycastle.jce.spec.ECParameterSpec spec = ecpriv.getParameters();if (spec != null) {len = spec.getN().bitLength();} else {// We support the key, but we don't know the key lengthlen = 0;}} else if (pk instanceof ECPublicKey) {final ECPublicKey ecpriv = (ECPublicKey) pk;final java.security.spec.ECParameterSpec spec = ecpriv.getParams();if (spec != null) {len = spec.getOrder().bitLength(); // does this really return something we expect?} else {// We support the key, but we don't know the key lengthlen = 0;}} else if (pk instanceof DSAPublicKey) {final DSAPublicKey dsapub = (DSAPublicKey) pk;if ( dsapub.getParams() != null ) {len = dsapub.getParams().getP().bitLength();} else {len = dsapub.getY().bitLength();}}return len;}
相关文章:
RSA加密与签名的区别
文章目录 一、签名验签原理二 RSAUtils 工具类三、通过x509Certificate来获取CA证书的基本信息四、 通过公钥获取公钥长度 一、签名验签原理 签名的本质其实就是加密,但是由于签名无需还原成明文,因此可以在加密前进行哈希处理。所以签名其实就是哈希加…...

arcgis js api 4.x通过TileLayer类加载arcgis server10.2发布的切片服务跨域问题的解决办法
1.错误复现 2.解决办法 2.1去https://github.com/Esri/resource-proxy 网站下载代理配置文件,我下载的是最新的1.1.2版本,这里根据后台服务器配置情况不同有三种配置文件,此次我用到的是DotNet和Java. 2.2 DotNet配置 2.2.1 对proxy文件增加…...
如何让chatGPT给出高质量的回答?
如何让chatGPT给出高质量的回答? ChatGPT从入门到进阶教程合集_哔哩哔哩_bilibili 公式 【指令词】【背景】【输入】【输出要求】 1. 指令词 ——精准任务or命令 如:简述、解释、翻译、总结、润色 2. 背景 ——补充信息 如:简述一篇讲解…...
Java后端开发(八)-- idea(2022版)将commit(未push)的 本地仓库 的 单条commit记录 进行撤销
目录 1.修改Test01类后,提交到本地仓库 。 2.commit成功后,在Git =》Log中会显示,commit记录...

Mysql架构解析,InnoDB架构概述。
MySQL架构解析 Mysql整体架构 MySQL整体架构如下图所示: MySQL逻辑系统架构分为4层: 应用层MySQL服务层存储引擎层系统文件层 下面将对各层的功能和组件进行介绍,并探讨一条语句的执行过程。 应用层 应用层是MySQL体系架构的最上层,它…...

jmeter如何测试websocket接口?
jmeter做接口测试,很多人都是做http协议的接口,就有很多人问websocket的接口怎么测试啊? 首先,我们要明白,websocket接口是什么接口。 然后,我们怎么用jmeter测试? jmeter要测试websocket接口…...

15 Transformer 框架概述
整体框架 机器翻译流程(Transformer) 通过机器翻译来做解释 给一个输入,给出一个输出(输出是输入的翻译的结果) “我是一个学生” --》(通过 Transformer) I am a student 流程 1 编码器和解…...

[架构之路-241]:目标系统 - 纵向分层 - 企业信息化与企业信息系统(多台企业应用单机组成的企业信息网络)
目录 前言: 一、什么是信息系统:计算机软件硬件系统 1.1 什么是信息 1.2 什么是信息系统 1.3 什么是信息技术 1.4 什么是信息化与信息化转型 1.5 什么是数字化与数字化转型(信息化的前提) 1.6 数字化与信息化的比较 1.7 …...
flink中使用异步函数的几个注意事项
背景 在flink系统中,我们为了补充某个流事件成一个完整的记录,经常需要调用外部接口获取一些配置数据,流事件结合这些配置数据就可以组合成一条完整的记录,然而如果同步调用外部系统接口来实现,那么会有很大的性能瓶颈…...

QML之Repeater 控件使用
Repeater 控件是 重复作用 根据 model中的index 数量进行重复 废话不说 直接看如何用 当model 为数字时 Rectangle{height: 1200width: 500visible: trueanchors.fill: parentColumn{spacing: 20Repeater{model: 10delegate: Rectangle{width: 60height: 20color: index%2 …...
哈希树讲解
哈希树(HashTree)是哈希(Hash)算法的一种延续。传统数据结构中对如何避免哈希冲突都有一定的描述和解释,但是这些描述和解释都是泛泛而谈,并没有提出比较好的解决方案。这里所提到的哈希树(HashTree)算法就是要提供一种在理论上和实际应用中均能有效地处…...
vue 项目启动后一直不断的刷新停不下来
新建的vue 项目,配置了代理后项目一直刷新,停不下来,各种查找最后发现是vue.config.js 中的热更新配置项目开启的原因 const {defineConfig } require(vue/cli-service) const AutoImport require(unplugin-auto-import/webpack) const Co…...

makesense在线yolov5标注
文章目录 一、创建图片文件夹和label.txt二、在线标注数据 参考文章博主:风吹落叶花飘荡 一、创建图片文件夹和label.txt 创建一个放置图片的文件夹images,存放需要标注的图片(图片最好重命名为1,2,3…避免后面混淆) 创建label.t…...

python 之 矩阵相关操作
文章目录 1. **创建矩阵**:2. **矩阵加法**:3. **矩阵乘法**:4. **矩阵转置**:5. **元素级操作**:6. **汇总统计**:7. **逻辑操作**: 理解你的需求,我将为每个功能写一个单独的代码块…...
敢问路在何方
从2022年进入软件行业到现在,二十二年眨眼之间过去了,依然奋斗在编码第一线,挣扎在第一线,借此程序员佳节之际回顾一下这许多年的历程。 由于本人混得实在不好,工作过的地方都用某单位某公司来代替实际的,到…...

复习mysql中的事务
一个事务的开始和结尾必须是 start transaction | commit; rollback 事务特性 1.原子性:多个操作打包成一个整体,要么全部执行,要么一个都不执行。 不过这里的“一个都不执行”并不是真正的全不执行,只是看起来与没执行一样。…...

力扣刷题 day52:10-22
1.数组拆分 给定长度为 2n 的整数数组 nums ,你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), ..., (an, bn) ,使得从 1 到 n 的 min(ai, bi) 总和最大。 返回该 最大总和 。 方法一:排序 #方法一:排序 def arrayPai…...

ELK概述部署和Filebeat 分布式日志管理平台部署
ELK概述部署、Filebeat 分布式日志管理平台部署 一、ELK 简介二、ELK部署2.1、部署准备2.2、优化elasticsearch用户拥有的内存权限2.3、启动elasticsearch是否成功开启2.4、浏览器查看节点信息2.5、安装 Elasticsearch-head 插件2.6、ELK Logstash 部署(在 Apache 节…...

分享一下我家网络机柜,家庭网络设备推荐
家里网络机柜搞了几天终于搞好了,非专业的,走线有点乱,勿喷。 从上到下的设备分别是: 无线路由器(当ap用):TL-XDR6088 插排:德木pdu机柜插排 硬盘录像机:TL-NVR6108-L8P 第二排左边…...
uboot移植之mx6ull_alientek_nand.h文件详解三
一. 简介 mx6ull_alientek_nand.h文件是 开发板的 uboot的一个配置文件。每个开发板都有一个 .h的配置文件。 mx6ull_alientek_nand.h 文件其实是 之前针对正点原子ALPHA开发板移植的 Uboot配置文件。 本文继上一篇文章的学习,地址如下:uboot移植之m…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...

GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
vue3 daterange正则踩坑
<el-form-item label"空置时间" prop"vacantTime"> <el-date-picker v-model"form.vacantTime" type"daterange" start-placeholder"开始日期" end-placeholder"结束日期" clearable :editable"fal…...

保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!
目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...
[特殊字符] 手撸 Redis 互斥锁那些坑
📖 手撸 Redis 互斥锁那些坑 最近搞业务遇到高并发下同一个 key 的互斥操作,想实现分布式环境下的互斥锁。于是私下顺手手撸了个基于 Redis 的简单互斥锁,也顺便跟 Redisson 的 RLock 机制对比了下,记录一波,别踩我踩过…...
用js实现常见排序算法
以下是几种常见排序算法的 JS实现,包括选择排序、冒泡排序、插入排序、快速排序和归并排序,以及每种算法的特点和复杂度分析 1. 选择排序(Selection Sort) 核心思想:每次从未排序部分选择最小元素,与未排…...