Spring Boot对访问密钥加密解密——RSA

news2025/1/2 20:10:08

场景

用户无需登录,仅仅根据给定的访问keyId和keySecret就可以访问接口。

  1. keyId 等可以明文发送(不涉及机密),后端直接从请求头读取。
  2. keySecret 不可明文,需要加密后放在另一个请求头(或请求体)里,比如 Encrypted-Data。
  3. timestamp + 6位随机值(nonce) 用来防重放或检验请求时效,需要在明文里也传递给后端,以方便后端做校验。
  4. 后端拿到 Encrypted-Data 后,使用私钥解密得到真正的 keySecret、然后再和明文传递过来的 timestamp、randomValue 做进一步匹配。
# 请求头中的信息
keyId
timestamp
randomValue
Encrypted-Data="{\"keySecret\":\"%s\",\"timestamp\":\"%s\",\"randomValue\":\"%s\"}"

客户端

前端调用:拼装并加密数据

假设我们有如下信息:

  • keyId:“myKeyId”
  • keySecret:“myKeySecret”(需加密)
  • timestamp:“20251225103449”(格式 yyyyMMddHHmmss)
  • randomValue:6 位随机数,如 “538201”
生成随机数 & 时间戳
function generateTimestamp() {
  // 生成形如 20251225103449 的日期字符串 (yyyyMMddHHmmss)
  const now = new Date();
  const yyyy = now.getFullYear();
  const MM = String(now.getMonth() + 1).padStart(2, '0');
  const dd = String(now.getDate()).padStart(2, '0');
  const HH = String(now.getHours()).padStart(2, '0');
  const mm = String(now.getMinutes()).padStart(2, '0');
  const ss = String(now.getSeconds()).padStart(2, '0');
  return `${yyyy}${MM}${dd}${HH}${mm}${ss}`;
}

function generateRandomValue6() {
  // 生成 6 位随机数
  return String(Math.floor(Math.random() * 1000000)).padStart(6, '0');
}


加密 keySecret

假设使用 jsencrypt 进行 RSA 加密,并把 keySecret + timestamp + randomValue 打包在一个 JSON 对象里加密。

import axios from 'axios';
import JSEncrypt from 'jsencrypt';

// RSA 公钥
const PUBLIC_KEY_PEM = `
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqh...
-----END PUBLIC KEY-----
`;

// 拼装并加密
function encryptKeyInfo(keySecret, timestamp, randomValue) {
  const payload = {
    keySecret,
    timestamp,
    randomValue
  };
  const jsonStr = JSON.stringify(payload);

  const jsEncrypt = new JSEncrypt();
  jsEncrypt.setPublicKey(PUBLIC_KEY_PEM);
  const encrypted = jsEncrypt.encrypt(jsonStr);
  if (!encrypted) {
    throw new Error("加密失败,请检查公钥格式或数据大小");
  }
  return encrypted; // Base64 编码的加密结果
}

// 发送请求示例
async function callSecureApi() {
  const keyId = "myKeyId";
  const timestamp = generateTimestamp(); 
  const randomValue = generateRandomValue6(); 
  const keySecret = "myKeySecret";  // 需要加密的机密

  // 把 keySecret、timestamp、randomValue 一并加密
  const encryptedData = encryptKeyInfo(keySecret, timestamp, randomValue);

  // 发请求时,除了 "Encrypted-Data" 外,把 keyId/timestamp/randomValue 也都明文放在 header
  // 这样后端可做交叉验证
  try {
    const resp = await axios.post(
      '/api/secure',
      {}, // body 可为空或随意
      {
        headers: {
          'keyId': keyId,
          'timestamp': timestamp,
          'randomValue': randomValue,
          'Encrypted-Data': encryptedData
        }
      }
    );
    console.log('后端返回:', resp.data);
  } catch (e) {
    console.error(e);
  }
}

Java调用

maven依赖

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.14</version>
</dependency>

代码调用

package com.demo.test;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import javax.crypto.Cipher;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.Random;


public class ThirdPartyCaller {

    // 假设你目标服务的地址
    private static final String BASE_URL = "http://localhost:13131";
    // 例如 "http://localhost:8080" 或生产环境 "https://yourdomain.com"

    public static void main(String[] args) {
        try {
            // 1) 先获取公钥(PEM 格式)
            String publicKeyPem = "-----BEGIN PUBLIC KEY-----\n" +
                    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs3ZYer3Ibej9o4IRlzkC\n" +
                    "2Hjk0roGuR4ckIafh/DZNHv/JfHgM7R10BLfTnkiGyrps7Lk36Gm9RnafduphCMX\n" +
                    "nG7nZdCYKt16aE4Tqb7FZ/pLz8WOjpxiSLA361w+knG9UCyytmhXjo6wZ8zxNoSd\n" +
                    "ePhMkGBVxwCbeZS9wldBbwpRpMY1Dsyve7C8COhEiWIFXz0ruMsskukCue2Q6nNh\n" +
                    "6dLIN17MtEqa7in7Q6xPvyNPsCkfI8PAvQqGO5thdZoTcT7XPHrBJesfuS8sSmtF\n" +
                    "xMPfI/Nke/KTykeDcf4PHKd0GP5c6+/p0XVkzxHm7sbFEEdII41e1Gd81gJ6bPQc\n" +
                    "dQIDAQAB\n" +
                    "-----END PUBLIC KEY-----";

            // 2) 解析 PEM 得到 PublicKey 对象
            PublicKey pubKey = parsePublicKeyFromPem(publicKeyPem);

            // 3) 准备要加密和要发送的字段
            String keyId = "qwertyuiopasdfghjklzxcvbnm";
            String keySecret = "demo@123!";
            String timestamp = generateTimestamp();         // yyyyMMddHHmmss
            String randomValue = generateRandomValue6();    // 6位随机数

            // 4) 组装 JSON,再用公钥 RSA 加密
            //   若与前端示例一致,可把 (keySecret, timestamp, randomValue) 放到 JSON
            String payloadJson = String.format(
                    "{\"keySecret\":\"%s\",\"timestamp\":\"%s\",\"randomValue\":\"%s\"}",
                    keySecret, timestamp, randomValue
            );
            String encryptedData = rsaEncrypt(pubKey, payloadJson);

            // 5) 调用 /api/secure 接口
            String result = callSecureApi(keyId, timestamp, randomValue, encryptedData);
            System.out.println("调用 /aliyun_sms/send 响应: " + result);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 第一步:解析 PEM 格式的公钥字符串,得到 PublicKey 对象
     * 如果服务器只返回纯Base64(无头尾),可根据实际情况调整。
     */
    private static PublicKey parsePublicKeyFromPem(String publicKeyPem) throws Exception {
        // 移除头尾行和换行,保留中间的Base64部分
        String base64 = publicKeyPem
                .replaceAll("-----BEGIN PUBLIC KEY-----", "")
                .replaceAll("-----END PUBLIC KEY-----", "")
                .replaceAll("\\s", "");

        byte[] decoded = Base64.getDecoder().decode(base64);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decoded);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(keySpec);
    }

    /**
     * 第二步:把要加密的JSON用RSA公钥加密
     */
    private static String rsaEncrypt(PublicKey publicKey, String data) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] encrypted = cipher.doFinal(data.getBytes());
        return Base64.getEncoder().encodeToString(encrypted);
    }

    /**
     * 生成时间戳 (yyyyMMddHHmmss)
     */
    private static String generateTimestamp() {
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        return LocalDateTime.now().format(fmt);
    }

    /**
     * 生成6位随机数 (000000~999999)
     */
    private static String generateRandomValue6() {
        Random random = new Random();
        int num = random.nextInt(1_000_000);
        return String.format("%06d", num);
    }

    /**
     * 第三步:调用 /api/secure 接口,并在header中带上 keyId, timestamp, randomValue, Encrypted-Data
     */
    private static String callSecureApi(String keyId, String timestamp,
                                        String randomValue, String encryptedData) throws IOException {
        String url = BASE_URL + "/aliyun_sms/send";
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost post = new HttpPost(url);

            // 设置header
            post.setHeader("keyId", keyId);
            post.setHeader("timestamp", timestamp);
            post.setHeader("randomValue", randomValue);
            post.setHeader("Encrypted-Data", encryptedData);
            post.setHeader("Content-Type", "application/json;charset=UTF-8");

            // 此示例没给 body,若需要可在 body 放更多数据
            //post.setEntity(new StringEntity("{\"name\":\"张三\"}", "UTF-8"));

            try (CloseableHttpResponse response = httpClient.execute(post)) {
                int statusCode = response.getStatusLine().getStatusCode();
                String respBody = EntityUtils.toString(response.getEntity(), "UTF-8");
                if (statusCode == 200) {
                    return respBody;
                } else {
                    throw new IOException("调用 /api/secure 失败, HTTP状态=" + statusCode + ", body=" + respBody);
                }
            }
        }
    }
}

服务端

RSA私钥解密

package com.demo.lcpd.utils;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;


@Component
public class RsaDecryptor {
    @Value("${security.rsa.privateKeyPem}")
    private String privateKeyPem;

    public String decrypt(String encryptedBase64) throws Exception {
        // 1) 去掉 PEM 格式里的头尾和换行,只保留真正的Base64部分
        String base64Key = removePemFormatting(privateKeyPem);

        // 2) 把Base64解码还原成私钥对象
        byte[] keyBytes = Base64.getDecoder().decode(base64Key);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

        // 3) 解密
        byte[] encryptedBytes = Base64.getDecoder().decode(encryptedBase64);
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decrypted = cipher.doFinal(encryptedBytes);

        return new String(decrypted, StandardCharsets.UTF_8);
    }

    private String removePemFormatting(String pem) {
        // 移除行首行尾 (-----BEGIN PRIVATE KEY----- / -----END PRIVATE KEY-----) 以及换行
        return pem
                .replaceAll("-----BEGIN PRIVATE KEY-----", "")
                .replaceAll("-----END PRIVATE KEY-----", "")
                .replaceAll("\\s", "");
    }

}

自定义拦截器

package com.demo.lcpd.intercept;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.demo.lcpd.utils.RsaDecryptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;


@Component
public class RsaAuthInterceptor implements HandlerInterceptor {
    @Resource
    private RsaDecryptor rsaDecryptor;

    private Map<String, String> keyStore = new HashMap<>();

    //添加允许通过的keyId和keySecret
    public RsaAuthInterceptor() {
        keyStore.put("qwertyuiopasdfghjklzxcvbnm", "demo@123!");
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String keyId = request.getHeader("keyId");
        String timestamp = request.getHeader("timestamp");
        String randomValue = request.getHeader("randomValue");
        String encryptedData = request.getHeader("Encrypted-Data");

        if (keyId == null || timestamp == null || randomValue == null || encryptedData == null) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Missing auth headers");
            return false;
        }

        try {
            // 2) RSA 私钥解密
            String decryptedJson = rsaDecryptor.decrypt(encryptedData);
            // decryptedJson 形如: {"keySecret":"myKeySecret","timestamp":"20251225103449","randomValue":"538201"}

            // 3) 解析 JSON
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode node = objectMapper.readTree(decryptedJson);

            String decryptedKeySecret = node.get("keySecret").asText();
            String decryptedTimestamp = node.get("timestamp").asText();
            String decryptedRandomValue = node.get("randomValue").asText();

            // 4) 与明文传递的 timestamp、randomValue 对比
            if (!timestamp.equals(decryptedTimestamp) || !randomValue.equals(decryptedRandomValue)) {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Timestamp or randomValue mismatch");
                return false;
            }

            // 5) 校验 keyId 是否存在,以及 keySecret 是否匹配
            if (!keyStore.containsKey(keyId)) {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid keyId");
                return false;
            }
            String expectedSecret = keyStore.get(keyId);
            if (!expectedSecret.equals(decryptedKeySecret)) {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid keySecret");
                return false;
            }

            // 6) 校验 timestamp 是否在合理范围 (例如 ±15分钟)
            if (!checkTimestampValid(timestamp)) {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Timestamp expired or invalid");
                return false;
            }

            // 7) 防重放:检查 (keyId + timestamp + randomValue) 是否已使用过
            // 这里仅示例,不做具体实现
            // boolean notUsed = checkAndMarkUsed(keyId, timestamp, randomValue);
            // if (!notUsed) {
            //     response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Replay attack detected");
            //     return;
            // }

            // 校验成功
            return true;
        } catch (Exception e) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Decryption or parse error");
            return false;
        }
    }

    private boolean checkTimestampValid(String timestampStr) {
        try {
            // 解析 yyyyMMddHHmmss
            DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
            LocalDateTime reqTime = LocalDateTime.parse(timestampStr, fmt);
            LocalDateTime now = LocalDateTime.now();
            // 假设 15 分钟有效期
            return !reqTime.isBefore(now.minusMinutes(15)) && !reqTime.isAfter(now.plusMinutes(15));
        } catch (Exception e) {
            return false;
        }
    }
}

注册拦截器

package com.demo.lcpd.conf;

import com.demo.lcpd.intercept.RsaAuthInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;


@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Resource
    private RsaAuthInterceptor rsaAuthInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(rsaAuthInterceptor)
                .addPathPatterns("/**");
    }
}

在 Java 中生成 RSA 公私钥

使用 KeyPairGenerator

public class RsaKeyGeneratorPemExample {
    public static void main(String[] args) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(2048);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        // Base64
        String publicKeyBase64 = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
        String privateKeyBase64 = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
    
        // 包装成 PEM
        String publicKeyPem = convertToPem(publicKeyBase64, true);
        String privateKeyPem = convertToPem(privateKeyBase64, false);
    
        System.out.println(publicKeyPem);
        System.out.println(privateKeyPem);
    }
    
    private static String convertToPem(String base64Key, boolean isPublic) {
        int lineLength = 64;
        StringBuilder sb = new StringBuilder();
        if (isPublic) {
            sb.append("-----BEGIN PUBLIC KEY-----\n");
        } else {
            sb.append("-----BEGIN PRIVATE KEY-----\n");
        }
        for (int i = 0; i < base64Key.length(); i += lineLength) {
            int end = Math.min(i + lineLength, base64Key.length());
            sb.append(base64Key, i, end).append("\n");
        }
        if (isPublic) {
            sb.append("-----END PUBLIC KEY-----\n");
        } else {
            sb.append("-----END PRIVATE KEY-----\n");
        }
        return sb.toString();
    }

}

运行后就能得到标准的 PEM 形式公私钥,类似:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFA...
...LQIDAQAB
-----END PUBLIC KEY-----

-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQ...
...2ww==
-----END PRIVATE KEY-----

这两个就是 PEM 格式的“公钥”和“私钥”。

前端使用的 PUBLIC_KEY_PEM 是什么?

可以是带头尾的 PEM

在前端(例如使用 jsencrypt)时,最常见的做法就是直接使用 PEM 格式:

const PUBLIC_KEY_PEM = -----BEGIN PUBLIC KEY----- MIIBIjANBgkqh... -----END PUBLIC KEY-----;

然后在加密时,像这样写:

import JSEncrypt from 'jsencrypt';

const jsEncrypt = new JSEncrypt();
// 直接 set PEM 格式的字符串即可
jsEncrypt.setPublicKey(PUBLIC_KEY_PEM);
const encrypted = jsEncrypt.encrypt("hello world");

jsencrypt 这个库支持 PEM 格式字符串(包含头尾)。也就是说,如果你在 Java 端生成了 PEM,直接整段复制到前端就行。

也可以是纯 Base64 字符串

有些前端库也允许你只传Base64 编码的公钥,而不含头尾。那你就可以给它传:

// 纯 Base64,不带 -----BEGIN----
const PUBLIC_KEY_BASE64 = "MIIBIjANBgkq…";

const jsEncrypt = new JSEncrypt();
jsEncrypt.setPublicKey(`-----BEGIN PUBLIC KEY-----
${PUBLIC_KEY_BASE64}
-----END PUBLIC KEY-----`);

要点总结

1.后端生成 RSA 公私钥:

  • 公钥用来给前端加密;
  • 私钥留在后端解密,不要泄露。

2.前端使用 jsencrypt:

  • PEM 格式(带 -----BEGIN PUBLIC KEY-----)的公钥即可;
  • 把要加密的字段 (keySecret, timestamp, randomValue) JSON 化 -> 调用 .encrypt(…) -> 得到密文(Base64)。

3.后端使用私钥解密:

  • 先去掉 PEM 的头尾,只保留 Base64 -> 解码 -> Cipher.getInstance(“RSA/ECB/PKCS1Padding”) 解密;
  • 拿到明文 JSON,解析出 keySecret, timestamp, randomValue;
  • 与前端同时传来的明文 timestamp, randomValue 做比对;再检查 keyId 对应的 keySecret 是否匹配。

4.时间戳 + 随机值:

  • 可做防重放;
  • 时间戳超时或重复使用同一 (keyId + timestamp + randomValue),则拒绝请求。

5.配合 HTTPS:

  • RSA 加密可以避免明文 keySecret 出现在网络上,但最佳实践还是要用 HTTPS,防止更多层面的攻击、劫持、篡改。

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

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

相关文章

王佩丰24节Excel学习笔记——第十八讲:Lookup和数组

【以 Excel2010 系列学习&#xff0c;用 Office LTSC 专业增强版 2021 实践】 【本章技巧】 地址栏公式可以使用 F9 查看&#xff0c;取消请按Esc键&#xff0c;或者公式前的红色叉&#xff1b;使用数组时一定要注意使用绝对引用&#xff0c;方便下拉&#xff1b;使用数组时一…

【hackmyvm】hacked靶机wp

tags: HMVrootkitDiamorphine Type: wp 1. 基本信息^toc 文章目录 1. 基本信息^toc2. 信息收集2.1. 端口扫描2.2. 目录扫描2.3. 获取参数 3. 提权 靶机链接 https://hackmyvm.eu/machines/machine.php?vmHacked 作者 sml 难度 ⭐️⭐️⭐️⭐️️ 2. 信息收集 2.1. 端口扫描…

【超级详细】七牛云配置阿里云域名详细过程记录

0. 准备一个阿里云域名&#xff0c;记得要备案&#xff01;&#xff01;&#xff01;&#xff01; 1. 创建七牛云存储空间 首先&#xff0c;登录七牛云控制台&#xff0c;创建一个新的存储空间&#xff08;Bucket&#xff09;。这个存储空间将用于存放你的文件&#xff0c;并…

【JDBC】转账案例

回顾 使用工具类查询表 需求&#xff1a; 查询student表的所有数据&#xff0c;把数据封装到一个集合中 数据准备 #创建表 CREATE TABLE student( sid INT, name VARCHAR(100), age INT, sex VARCHAR(100) ) #插入数据 INSERT INTO student VALUES(1,张三,18,女),(2…

HTML——14. 超链接四种状态

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>超链接</title></head><body><a href"https://ai.m.taobao.com" target"_blank">淘宝</a><br /><a href"…

微信V3支付报错 平台证书及平台证书序列号

1.平台证书及平台证书序列号设置错误报错&#xff1a; 错误1&#xff1a; Verify the response’s data with: timestamp1735184656, noncea5806b8cabc923299f8db1a174f3a4d0, signatureFZ5FgD/jtt4J99GKssKWKA/0buBSOAbWcu6H52l2UqqaJKvrsNxvodB569ZFz5G3fbassOQcSh5BFq6hvE…

MusicFree - 免费播放全网歌曲!无广告开源网络音乐聚合播放器 (安卓电脑版)

大家平常听歌可能都会在 QQ 音乐、网易云音乐、酷狗、喜马拉雅等不同平台来回切换&#xff0c;体验其实很烦。曾经推荐过不少“聚合”音乐应用&#xff0c;比如 洛雪音乐助手、Listen1 等等。 最近又有一个新选择了&#xff01;MusicFree 是一款免费开源清爽无广告的音乐播放器…

C++的第一个程序

前言 在学习c之前&#xff0c;你一定还记得c语言的第一个程序 当时刚刚开始进行语言学习 因此告诉到&#xff0c;仅仅需要记住就可以 #include <stdio.h>int main(){printf("Hello World");return 0; }而对于c中的第一个程序&#xff0c;似乎有所变化 C的…

代码随想录算法【Day1】

Day1 1.掌握二分法边界值判断&#xff0c;是根据写法来的; 2.删除数组元素的双指针和暴力解法; 3.灵活使用双指针方法 704 二分法 以前对于边界的问题非常纠结&#xff0c;到底是<还是<&#xff0c;以及是mid还是mid-1。 通过视频讲解&#xff0c;得知二分法的两种…

探索CSDN博客数据:使用Python爬虫技术

探索CSDN博客数据&#xff1a;使用Python爬虫技术 在数字化的浪潮中&#xff0c;数据的获取与分析变得日益关键。CSDN作为中国领先的IT社区和服务平台&#xff0c;汇聚了海量的技术博客与文章&#xff0c;成为一座蕴藏丰富的数据宝库。本文将引领您穿梭于Python的requests和py…

实战案例——ZooKeeper集群部署(新手教程超详细)

案例目标 了解ZooKeeper分布式应用程序协调服务使用3台机器搭建ZooKeeper集群使用ZooKeeper集群 案例分析 规划节点 ZooKeeper集群节点规划 Ip 主机名 节点 192.168.110.10 zookeeper1 集群节点 192.168.110.20 zookeeper2 集群节点 192.168.110.30 zookeeper3 …

如果你的网站是h5网站,如何将h5网站变成小程序-除开完整重做方法如何快速h5转小程序-h5网站转小程序的办法-优雅草央千澈

如果你的网站是h5网站&#xff0c;如何将h5网站变成小程序-除开完整重做方法如何快速h5转小程序-h5网站转小程序的办法-优雅草央千澈 h5如何转小程序 如果当年你们开发网站是用的h5但是没有开发小程序&#xff0c;也没有使用uniapp这样的混开框架&#xff0c;但是目前根据业务需…

阿里云redis内存优化——PCP数据清理

在阿里云安装了一个redis节点&#xff0c;今天使用时忽然想着点击了一下分析内存。好家伙&#xff0c;居然崩出了一个30多M的块出来。问题是我本地安装的redis没有这个啊&#xff0c;怎么奇怪冒出这个来了。 本着把系统用干榨尽的态度&#xff0c;研究了下这个问题的来源。网上…

学系C++:循环练习案例

一&#xff0c;猜数字 案例描述&#xff1a;系统随机生成一个1到100之间的数字&#xff0c;玩家进行猜测&#xff0c;如果猜错&#xff0c;提示玩家数字过大或过小&#xff0c;如果猜对恭喜玩家胜利&#xff0c;并且退出游戏。 #include <iostream> using namespace st…

六大基础深度神经网络之CNN

左侧是传统卷积网络输入的是一列像素点&#xff0c;右侧是卷积神经网络&#xff0c;输入的是具有长宽通道数的原始图像 下图为整体架构。卷积层可以认为提取特征&#xff0c;池化层是压缩特征。全连接层是把图像展平然后计算10个类别的概率值 给出一张图像不同区域的特征不同&a…

SemiDrive E3 MCAL 开发系列(6)– Icu 模块的使用

一、 概述 本文将会介绍 SemiDrive E3 MCAL Icu 模块的简介以及基本配置&#xff0c;其中还会涉及到 Xtrg 模块的配置。此外会结合实际操作的介绍&#xff0c;帮助新手快速了解并掌握这个模块的使用&#xff0c;文中的 MCAL 是基于 PTG3.0 的版本&#xff0c;开发板是官方的 …

嵌入式入门Day35

网络编程 Day2 套接字socket基于TCP通信的流程服务器端客户端TCP通信API 基于UDP通信的流程服务器端客户端 作业 套接字socket socket套接字本质是一个特殊的文件&#xff0c;在原始的Linux中&#xff0c;它和管道&#xff0c;消息队列&#xff0c;共享内存&#xff0c;信号等…

【Redis】:初识Redis

1.1 盛赞 Redis Redis 是⼀种基于键值对&#xff08;key-value&#xff09;的 NoSQL 数据库&#xff0c;与很多键值对数据库不同的是&#xff0c;Redis 中的值可以是由 string&#xff08;字符串&#xff09;、hash&#xff08;哈希&#xff09;、list&#xff08;列表&#xf…

MATLAB 车牌自动识别系统设计 图像分割与图像增强方法 车牌识别

一 车牌自动识别系统总体设计 基于matlab的车牌识别系统&#xff0c;第一种方法采用图像分割与图像增强的方法&#xff0c;采集的车牌后将图像传入程序中&#xff0c;对图像进行处理后将车牌号提取出来&#xff0c;然后与数据库的样本进行对比后输出结果。 本课题拟采用的思路&…

Windows下C++使用SQLite

1、安装 进入SQLite Download Page页面&#xff0c;下载sqlite-dll-win-x86-*.zip、sqlite-amalgamation-*.zip、sqlite-tools-win-x64-*.zip三个包&#xff0c;这三个包里分别包含dll文件和def文件、头文件、exe工具。 使用vs命令行工具生成.lib文件&#xff1a;进入dll和def文…