加密与安全_优雅存储二要素(AES-256-GCM )

news2024/11/15 12:26:09

文章目录

  • 什么是二要素
  • 如何保护二要素(姓名和身份证)
    • 加密算法分类
    • 场景选择
    • 算法选择
      • AES - ECB 模式 (不推荐)
      • AES - CBC 模式 (推荐)
      • GCM(Galois/Counter Mode)
        • AES-256-GCM简介
        • AES-256-GCM工作原理
        • 安全优势
      • 应用场景
      • 其他模式 和 敏感数据加密建议
  • Code

在这里插入图片描述


什么是二要素

二要素(姓名和身份证)是敏感数据,,很多网站仅仅依靠二要素来确认你是谁,若以明文形式存储在数据库中,存在被攻破的风险。若这些信息被不法分子获取, 后果严重。


如何保护二要素(姓名和身份证)

单向散列算法,如MD5、SHA-256等,虽然可以对数据生成唯一的指纹,但由于其不可逆,无法用于加密需要解密的数据。因此,它不适合用于对二要素信息(如姓名和身份证)进行加密保存。

在此情况下,需要选择真正的加密算法来实现数据的加密存储与解密。

加密算法分类

  1. 对称加密算法

    • 对称加密算法依赖于一个相同的密钥,既用于加密,也用于解密。常见的对称加密算法包括AES、DES、3DES等。
    • 在通信场景中,加密方和解密方必须事先共享密钥,双方才能进行加密和解密。密钥的共享过程是该加密方式的关键挑战之一,因为如果密钥在传输过程中被窃取或泄露,加密数据就容易被解密,安全性将大打折扣。
    • 优点:对称加密的主要优势在于加密和解密的速度非常快,特别适合需要高效处理大量数据的场景。
    • 缺点:密钥分发的安全性是对称加密的主要隐患。如果在通信中,密钥传输不当导致泄露,攻击者可以利用该密钥轻松解密数据。
  2. 非对称加密算法

    • 非对称加密算法由一对密钥构成,分别为公钥(加密密钥)和私钥(解密密钥)。常见的非对称加密算法有RSA、DSA等。
    • 公钥可以任意公开,而私钥必须保持私密。使用公钥加密的数据只能由对应的私钥解密。因此,通信双方只需共享公钥即可,无需直接传输私钥,从而避免了密钥泄露的问题。
    • 优点:非对称加密解决了密钥分发的安全性问题,适合用在双方未建立密钥共享机制的场景。
    • 缺点:加密和解密的速度相对较慢,特别是在处理大数据量时性能不足。

场景选择

在需要加密保存二要素信息的场景下,加密和解密都是由同一个服务端程序执行,双方并不需要通过网络传输密钥,因此密钥分发的安全性问题不是关键点。相对而言,对称加密算法具有速度快、效率高的优势,更适合在服务端加密存储二要素数据

因此,尽管非对称加密在解决密钥传输安全性上有独特优势,但在保存敏感数据的场景中,采用对称加密算法(如AES-CBC或AES-CTR模式)更为合适。

算法选择

对称加密常用算法有 DES3DESAES

  • DES 已被证明不安全,破解时间很短,不推荐使用。
  • 3DES 通过三次 DES 串联调用解决了 DES 的安全性问题,但速度较慢,也不推荐使用。
  • AES 是目前公认安全且高效的算法,采用 Rijndael 作为标准。

AES 是目前较为主流的对称加密算法,兼具高安全性和高性能。AES 是由 NIST 选拔出的 Rijndael 算法作为标准,支持分组加密模式。AES 每次处理 128 位明文,生成相应的 128 位密文。对于较长的明文,需要通过分组迭代加密。

AES - ECB 模式 (不推荐)

在这里插入图片描述

加密一段包含 16 个字符的字符串,得到密文 A;然后把这段字符串复制一份成为一个32 个字符的字符串,再进行加密得到密文 B。我们验证下密文 B 是不是重复了一遍的密文 A。

模拟银行转账的场景,假设整个数据由发送方账号、接收方账号、金额三个字段构成。我们尝试改变密文中数据的顺序来操纵明文

   private static final String KEY = "secretkey1234567";

   @GetMapping("ecb")
    public void ecb() throws Exception {
        Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
        test(cipher, null);
    }


   private static SecretKeySpec setKey(String secret) {
        return new SecretKeySpec(secret.getBytes(), "AES");
    }


   private static void test(Cipher cipher, AlgorithmParameterSpec parameterSpec) throws Exception {
	        cipher.init(Cipher.ENCRYPT_MODE, setKey(KEY), parameterSpec);
	        System.out.println("一次:" + Hex.encodeHexString(cipher.doFinal("abcdefghijklmnop".getBytes())));
	        System.out.println("两次:" + Hex.encodeHexString(cipher.doFinal("abcdefghijklmnopabcdefghijklmnop".getBytes())));
	        byte[] sender = "1000000000012345".getBytes();
	        byte[] receiver = "1000000000034567".getBytes();
	        byte[] money = "0000000010000000".getBytes();
	
	        //加密发送方账号
	        System.out.println("发送方账号:" + Hex.encodeHexString(cipher.doFinal(sender)));
	        //加密接收方账号
	        System.out.println("接收方账号:" + Hex.encodeHexString(cipher.doFinal(receiver)));
	        //加密金额
	        System.out.println("金额:" + Hex.encodeHexString(cipher.doFinal(money)));
	        byte[] result = cipher.doFinal(ByteUtils.concatAll(sender, receiver, money));
	        //加密三个数据
	        System.out.println("完整数据:" + Hex.encodeHexString(result));
	        byte[] hack = new byte[result.length];
	        //把密文前两段交换
	        System.arraycopy(result, 16, hack, 0, 16);
	        System.arraycopy(result, 0, hack, 16, 16);
	        System.arraycopy(result, 32, hack, 32, 16);
	        cipher.init(Cipher.DECRYPT_MODE, setKey(KEY), parameterSpec);
	        //尝试解密
	        System.out.println("原始明文:" + new String(ByteUtils.concatAll(sender, receiver, money)));
	        System.out.println("操纵密文:" + new String(cipher.doFinal(hack)));
	    }

输出

一次:a6025aaadd429e8c13073fc3512a7250
两次:a6025aaadd429e8c13073fc3512a7250a6025aaadd429e8c13073fc3512a7250
发送方账号:fdfc03515d95e2fa33edc9ca67cf43ae
接收方账号:e70eecf4baa8decf117d294e12d850c0
金额:f317ed23783f4babb607bd88ba076d0c
完整数据:fdfc03515d95e2fa33edc9ca67cf43aee70eecf4baa8decf117d294e12d850c0f317ed23783f4babb607bd88ba076d0c
原始明文:100000000001234510000000000345670000000010000000
操纵密文:100000000003456710000000000123450000000010000000

如上代码示例展示了 ECB 模式的漏洞,攻击者可以在不解密的情况下操纵密文,实现对敏感数据(如银行转账信息)的修改。 -----> 在不知道密钥的情况下,我们操纵密文实现了对明文数据的修改,对调了发送方账号和接收方账号

原始明文:100000000001234510000000000345670000000010000000
操纵密文:100000000003456710000000000123450000000010000000

代码运行的结果证明了:

  • 重复的明文生成相同的密文。

  • 攻击者可以通过调换密文分组的顺序,达到修改明文数据的效果。

  • 重复性问题:如果明文中有重复的分组,密文中也会出现重复,这会暴露明文的模式,存在规律性。

  • 独立分组问题:每个分组独立加密和解密,攻击者可以通过交换密文分组的顺序来操控明文内容。

因此,ECB 模式简单但不安全,不推荐使用。


AES - CBC 模式 (推荐)

CBC 模式,在解密或解密之前引入了 XOR 运算,第一个分组使用外部提供的初始化向量IV,从第二个分组开始使用前一个分组的数据,这样即使明文是一样的,加密后的密文也是不同的,并且分组的顺序不能任意调换。这就解决了 ECB 模式的缺陷.

在这里插入图片描述

把之前的代码修改为 CBC 模式,再次进行测试

 private static final String initVector = "abcdefghijklmnop";


 @GetMapping("cbc")
    public void cbc() throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
        test(cipher, iv);
    }

可以看到,相同的明文字符串复制一遍得到的密文并不是重复两个密文分组,并且调换密文分组的顺序无法操纵明文。

一次:6fa7a7b2c0979abecc1b59fe17b663c6
两次:6fa7a7b2c0979abecc1b59fe17b663c6e873cb4abb4b46b76cb748447373103c
发送方账号:ff4f74de614be6905951fa2ac68a529a
接收方账号:0dfdd3116d26dac4a7349167dfa0ce0a
金额:5521773b79160a1a51b9d8f8bfb0a346
完整数据:ff4f74de614be6905951fa2ac68a529abb54065906129619b122c978541f0076347086b16d09934e4f9d9dc4ab942af0
原始明文:100000000001234510000000000345670000000010000000
SD�A�x�%B[3t+B�Wi@��Cb��b�

GCM(Galois/Counter Mode)

AES-256-GCM简介

GCM(Galois/Counter Mode)是一种结合计数器模式(Counter Mode)和Galois域认证的分组加密模式。它不仅能够提供高效的加密服务,还能实现消息认证(即验证消息的完整性和真实性)。与传统的CBC模式不同,GCM模式可以并行处理,极大提升了性能,特别适合高吞吐量的环境。

这是一种 AEADAuthenticated Encryption with Associated Data)认证加密算法,除了能实现普通加密算法提供的保密性之外,还能实现可认证性和密文完整性,是目前最推荐的 AES 模式。

使用类似 GCM 的 AEAD 算法进行加解密,除了需要提供初始化向量和密钥之外,还可以提供一个 AAD(附加认证数据,additional authenticated data),用于验证未包含在明文中的附加信息,解密时不使用加密时的 AAD 将解密失败。其实,GCM 模式的内部使用的就是 CTR 模式,只不过还使用了 GMAC 签名算法,对密文进行签名实现完整性校验。


AES-256-GCM工作原理

AES-256-GCM结合了AES-256加密算法和GCM模式,具备如下功能:

  • 加密:数据通过AES-256加密算法被加密。
  • 消息认证码(MAC):在加密的同时,GCM模式会生成一个128位的消息认证码,用于验证数据的完整性和真实性。这一特性可以防止数据被篡改。
  • 附加数据:GCM支持附加认证数据(AAD),这部分数据不会被加密,但会被用于认证。例如,网络协议中的头部信息可以作为AAD进行保护。
安全优势

相较于其他常见的模式,如CBC(Cipher Block Chaining),AES-256-GCM提供了显著的优势:

  • 并行处理:GCM模式允许多线程并行处理,加速了加密和解密过程,非常适合高性能需求的场景。
  • 集成认证:GCM不仅加密数据,还生成认证标签,保证数据的完整性和真实性。相比之下,像CBC这样的模式需要单独实现消息认证。
  • 防御重放攻击:GCM模式通过计数器的设计,有效防止了重放攻击和其他类似的攻击手段。

应用场景

AES-256-GCM的广泛应用场景包括:

  • TLS/SSL加密:TLS 1.3推荐使用AES-GCM模式进行数据加密,以确保网络通信的安全性。
  • VPN:许多VPN协议(如IPSec)使用AES-256-GCM进行数据传输加密。
  • 云存储加密:云服务供应商在存储敏感数据时经常采用AES-256-GCM,以确保数据的安全性和完整性。

其他模式 和 敏感数据加密建议

除了 ECB 模式外,AES 还有 CBCCFBOFBCTR 模式。推荐使用 CBCCTR 模式。ECB 和 CBC 模式需要设置合适的填充方式来处理超过一个分组的数据。

此外,对于敏感数据加密,建议:

  1. 不要在代码中写死密钥或初始化向量(IV),应确保密钥和 IV 唯一、独立、且每次都变化。
  2. 使用独立的加密服务来管理密钥,避免将密钥与密文存储在同一个数据库中,确保加密服务有严格的管控标准。
  3. 数据库中不应保存明文敏感信息,可以存储脱敏数据,并在普通查询时使用脱敏信息。

Code

接下来,我们按照如上策略完成相关代码实现:

第一步,对于用户姓名和身份证,我们分别保存三个信息,脱敏后的明文、密文和加密ID。加密服务加密后返回密文和加密 ID,随后使用加密 ID 来请求加密服务进行解密

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.Id;

@Data
@Entity
public class UserData {
    @Id
    private Long id;
    private String idcard;//脱敏的身份证
    private Long idcardCipherId;//身份证加密ID
    private String idcardCipherText;//身份证密文
    private String name;//脱敏的姓名
    private Long nameCipherId;//姓名加密ID
    private String nameCipherText;//姓名密文
}


第二步,加密服务数据表保存加密 ID、初始化向量和密钥。加密服务表中没有密文,实现了密文和密钥分离保存.

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

import static javax.persistence.GenerationType.AUTO;

@Data
@Entity
public class CipherData {
    @Id
    @GeneratedValue(strategy = AUTO)
    private Long id;
    private String iv;//初始化向量
    private String secureKey;//密钥
}


第三步,加密服务使用 GCM 模式( Galois/Counter Mode)的 AES-256 对称加密算法,也就是 AES-256-GCM

接下来,我们实现基于 AES-256-GCM 的加密服务,包含下面的主要逻辑:

  • 加密时允许外部传入一个 AAD 用于认证,加密服务每次都会使用新生成的随机值作为密钥和初始化向量。
  • 在加密后,加密服务密钥和初始化向量保存到数据库中,返回加密 ID 作为本次加密的标识。
  • 应用解密时,需要提供加密 ID、密文和加密时的 AAD 来解密。加密服务使用加密 ID,从数据库查询出密钥和初始化向量。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Base64;

@Service
public class CipherService {
    //密钥长度
    public static final int AES_KEY_SIZE = 256;
    //初始化向量长度
    public static final int GCM_IV_LENGTH = 12;
    //GCM身份认证Tag长度
    public static final int GCM_TAG_LENGTH = 16;
    @Autowired
    private CipherRepository cipherRepository;

    //内部加密方法
    public static byte[] doEncrypt(byte[] plaintext, SecretKey key, byte[] iv, byte[] aad) throws Exception {
        //加密算法
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        //Key规范
        SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
        //GCM参数规范
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
        //加密模式
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
        //设置aad
        if (aad != null)
            cipher.updateAAD(aad);
        //加密
        byte[] cipherText = cipher.doFinal(plaintext);
        return cipherText;
    }

    //内部解密方法
    public static String doDecrypt(byte[] cipherText, SecretKey key, byte[] iv, byte[] aad) throws Exception {
        //加密算法
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        //Key规范
        SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
        //GCM参数规范
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
        //解密模式
        cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
        //设置aad
        if (aad != null)
            cipher.updateAAD(aad);
        //解密
        byte[] decryptedText = cipher.doFinal(cipherText);
        return new String(decryptedText);
    }

    //加密入口
    public CipherResult encrypt(String data, String aad) throws Exception {
        //加密结果
        CipherResult encryptResult = new CipherResult();
        //密钥生成器
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        //生成密钥
        keyGenerator.init(AES_KEY_SIZE);
        SecretKey key = keyGenerator.generateKey();
        //IV数据
        byte[] iv = new byte[GCM_IV_LENGTH];
        //随机生成IV
        SecureRandom random = new SecureRandom();
        random.nextBytes(iv);
        //处理aad
        byte[] aaddata = null;
        if (!StringUtils.isEmpty(aad))
            aaddata = aad.getBytes();
        //获得密文
        encryptResult.setCipherText(Base64.getEncoder().encodeToString(doEncrypt(data.getBytes(), key, iv, aaddata)));
        //加密上下文数据
        CipherData cipherData = new CipherData();
        //保存IV
        cipherData.setIv(Base64.getEncoder().encodeToString(iv));
        //保存密钥
        cipherData.setSecureKey(Base64.getEncoder().encodeToString(key.getEncoded()));
        cipherRepository.save(cipherData);
        //返回本地加密ID
        encryptResult.setId(cipherData.getId());
        return encryptResult;
    }

    //解密入口
    public String decrypt(long cipherId, String cipherText, String aad) throws Exception {
        //使用加密ID找到加密上下文数据
        CipherData cipherData = cipherRepository.findById(cipherId).orElseThrow(() -> new IllegalArgumentException("invlaid cipherId"));
        //加载密钥
        byte[] decodedKey = Base64.getDecoder().decode(cipherData.getSecureKey());
        //初始化密钥
        SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
        //加载IV
        byte[] decodedIv = Base64.getDecoder().decode(cipherData.getIv());
        //处理aad
        byte[] aaddata = null;
        if (!StringUtils.isEmpty(aad))
            aaddata = aad.getBytes();
        //解密
        return doDecrypt(Base64.getDecoder().decode(cipherText.getBytes()), originalKey, decodedIv, aaddata);
    }
}

第四步,分别实现加密和解密接口用于测试。

可以让用户选择,如果需要保护二要素的话,就自己输入一个查询密码作为 AAD。系统需要读取用户敏感信息的时候,还需要用户提供这个密码,否则无法解密。这样一来,即使黑客拿到了用户数据库的密文、加密服务的密钥和 IV,也会因为缺少 AAD 无法解密.

 @Autowired
 private CipherService cipherService;

 // 加密 
 @GetMapping("right")
    public UserData right(@RequestParam(value = "name", defaultValue = "小工匠") String name,
                          @RequestParam(value = "idcard", defaultValue = "300000000000001234") String idCard,
                          @RequestParam(value = "aad", required = false) String aad) throws Exception {
        UserData userData = new UserData();
        userData.setId(1L);
        //脱敏姓名
        userData.setName(chineseName(name));
        //脱敏身份证
        userData.setIdcard(idCard(idCard));
        //加密姓名
        CipherResult cipherResultName = cipherService.encrypt(name, aad);
        userData.setNameCipherId(cipherResultName.getId());
        userData.setNameCipherText(cipherResultName.getCipherText());
        //加密身份证
        CipherResult cipherResultIdCard = cipherService.encrypt(idCard, aad);
        userData.setIdcardCipherId(cipherResultIdCard.getId());
        userData.setIdcardCipherText(cipherResultIdCard.getCipherText());
        return userRepository.save(userData);
    }


	// 解密 
   @GetMapping("read")
    public void read(@RequestParam(value = "aad", required = false) String aad) throws Exception {
        UserData userData = userRepository.findById(1L).get();
        log.info("name : {} idcard : {}",
                cipherService.decrypt(userData.getNameCipherId(), userData.getNameCipherText(), aad),
                cipherService.decrypt(userData.getIdcardCipherId(), userData.getIdcardCipherText(), aad));

    }

  // 脱敏身份证
   private static String idCard(String idCard) {
        String num = StringUtils.right(idCard, 4);
        return StringUtils.leftPad(num, StringUtils.length(idCard), "*");
    }

 // 脱敏姓名
    public static String chineseName(String chineseName) {
        String name = StringUtils.left(chineseName, 1);
        return StringUtils.rightPad(name, StringUtils.length(chineseName), "*");
    }


启动服务,访问 http://localhost:45678/storeidcard/right

在这里插入图片描述

访问解密接口: http://localhost:45678/storeidcard/read
在这里插入图片描述

如果AAD错误

在这里插入图片描述

经过这样的设计,二要素就比较安全了。黑客要查询用户二要素的话,需要同时拿到密文、IV+ 密钥、AAD。而这三者可能由三方掌管,要全部拿到比较困难。

在这里插入图片描述

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

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

相关文章

LeetcodeTop100 刷题总结(一)

LeetCode 热题 100:https://leetcode.cn/studyplan/top-100-liked/ 文章目录 一、哈希1. 两数之和49. 字母异位词分组128. 最长连续序列 二、双指针283. 移动零11. 盛水最多的容器15. 三数之和42. 接雨水(待完成) 三、滑动窗口3. 无重复字符的…

TCADE--基于迁移成分分析和差分进化的多目标多任务优化

TCADE–基于迁移成分分析和差分进化的多目标多任务优化 title: Multitasking multiobjective optimization based on transfer component analysis author: Ziyu Hua, Yulin Li, Hao Sun, Xuemin Ma. journal: Information Sciences (Ins)…

最优化理论与自动驾驶(十):纯跟踪算法原理、公式及代码演示

纯跟踪算法(Pure Pursuit Algorithm)是一种用于路径跟踪的几何控制算法,广泛应用于自动驾驶、机器人导航等领域。其基本思想是通过选择预定路径上的目标点(预瞄点),并控制转向角,使车辆不断逼近…

用于稀疏自适应深度细化的掩码空间传播网络 CVPR2024

目录 Masked Spatial Propagation Network for Sparsity-Adaptive Depth Refinement (CVPR 2024)用于稀疏自适应深度细化的掩码空间传播网络1 介绍2 算法流程2.1 问题建模2.2 Guidance Network2.3 MSPN 模块 3 实验结果3.1 稀疏度自适应深度细化对比试验…

COMDEL电源CX2500S RF13.56MHZ RF GENERATOR手侧

COMDEL电源CX2500S RF13.56MHZ RF GENERATOR手侧

如何让虚拟机的服务被主机访问

当我们在虚拟机上写了一个服务器,在宿主机访问时,出现无法访问的情况。这可能是虚拟机网络的设置问题。 查看虚拟机防火墙是否关闭 在终端输入: systemctl status firewalld 如果是active就说明防火墙是开启的,需要关闭。 输入…

高级I/O知识分享【epoll || Reactor ET,LT模式】

博客主页:花果山~程序猿-CSDN博客 文章分栏:Linux_花果山~程序猿的博客-CSDN博客 关注我一起学习,一起进步,一起探索编程的无限可能吧!让我们一起努力,一起成长! 目录 一,接口 epo…

SpringBoot 消息队列RabbitMQ 消息可靠性 数据持久化 与 LazyQueue

介绍 在默认情况下,RabbitMQ会将接收到的信息保存在内存中以降低消息收发的延迟 一旦MO宕机,内存中的消息会丢失内存空间有限,当消费者故障或处理过慢时,会导致消息积压,引发MQ阻塞 在消息队列运行的过程中&#xf…

LeetCode 815.公交路线(BFS广搜 + 建图)(中秋快乐啊)

给你一个数组 routes ,表示一系列公交线路,其中每个 routes[i] 表示一条公交线路,第 i 辆公交车将会在上面循环行驶。 例如,路线 routes[0] [1, 5, 7] 表示第 0 辆公交车会一直按序列 1 -> 5 -> 7 -> 1 -> 5 -> …

物理感知扩散的 3D 分子生成模型 - PIDiff 评测

PIDiff 是一个针对蛋白质口袋特异性的、物理感知扩散的 3D 分子生成模型,通过考虑蛋白质-配体结合的物理化学原理来生成分子,在原理上,生成的分子可以实现蛋白-小分子的自由能最小。 一、背景介绍 PIDiff 来源于延世大学计算机科学系的 Sang…

vue2基础系列教程之v-model及面试高频问题

v-model是表单组件里面的核心知识点&#xff0c;这个指令给我们写表单业务带来了很大的方便。 元素标签上的 v-model 指令用于双向绑定数据,它是一个语法糖&#xff0c;可以用于代替 v-bind:value 和 input 例如&#xff1a;<input v-model"message" placeholder…

VTD激光雷达(6)——06_OptiX_Variables

文章目录 前言一、总结 前言 感谢VTD官方学习资料 一、 1、 总结 学海无涯回头是岸

curl格式化json之jq工具?

jq 是一个轻量级的命令行工具&#xff0c;用于解析、操作和格式化 JSON 数据。它类似于 sed 或 awk&#xff0c;但专门用于处理 JSON 格式。使用 jq&#xff0c;你可以从复杂的 JSON 数据中提取所需的信息&#xff0c;格式化输出&#xff0c;进行数据筛选&#xff0c;甚至修改 …

正点原子阿尔法ARM开发板-IMX6ULL(六)——通过官方SDK完成实验

文章目录 一、引言1.1 cc.h1.2 main.c1.2 fsl_common.h、MCIMX6Y2.h、fsl_iomuxc.h1.3 对于宏定义能多个参数 其他 一、引言 在开发过程中&#xff0c;如果一个人来写寄存器、汇编等东西&#xff0c;会变得特别繁琐&#xff0c;好在官方NXP官方给出了SDK包&#xff0c; 1.1 c…

牛客周赛 Round 60(下)

构造序列 题目描述 登录—专业IT笔试面试备考平台_牛客网 运行代码 #include <iostream> #include<stdio.h> #include<math.h> using namespace std; int main() {int n, m;cin >> n >> m;int minVal min(n, m);int maxVal max(n, m);cout …

如何解决“json schema validation error ”错误? -- HarmonyOS自学6

一. 问题描述 DevEco Studio工程关闭后&#xff0c;再重新打开时&#xff0c;出现了如下错误提示&#xff1a; json schema validation error 原因&#xff1a; index.visual或其他visual文件中的left等字段的值为负数时&#xff0c;不能以”-0.x“开头&#xff0c;否则就会…

了解华为云容器引擎(Cloud Container Engine)

1.什么是云容器引擎&#xff1f; 云容器引擎&#xff08;Cloud Container Engine&#xff0c;简称CCE&#xff09;提供高度可扩展的、高性能的企业级Kubernetes集群。借助云容器引擎&#xff0c;您可以在华为云上轻松部署、管理和扩展容器化应用程序。云容器引擎是一个企业级的…

【Android】使用Room数据库解决本地持久化

【Android】使用Room数据库解决本地持久化 Room概述 Room 是一个持久性库&#xff0c;属于 Android Jetpack 的一部分。 Room 是 SQLite 数据库之上的一个抽象层。Room 并不直接使用 SQLite&#xff0c;而是负责简化数据库设置和配置以及与数据库交互方面的琐碎工作。此外&a…

ElasticSearch-2-核心语法集群高可用实战-Week2

ES批量操作 1.批量获取文档数据 这里多个文档是指&#xff0c;批量操作多个文档&#xff0c;搜索查询文档将在之后的章节讲解 批量获取文档数据是通过_mget的API来实现的 (1)在URL中不指定index和type 请求方式&#xff1a;GET 请求地址&#xff1a;_mget 功能说明 &#…

数据结构 - 树与二叉树

一.普通有序树的定义 1.树的概念及特性 二.二叉树的定义 1.二叉树的性质 2.二叉树的分类 ①.满二叉树 每一层的结点数都为最大值 ②.完全二叉树 完全二叉树是由满二叉树&#xff0c;从下向上&#xff0c;从右向左依次擦除若干个结点 3.二叉树的结构 三.链式二叉树的创建 1.链式…