Go语言:加密与解密详解 - 详解

news/2025/10/21 11:10:02/文章来源:https://www.cnblogs.com/tlnshuju/p/19154545

Go语言:加密与解密详解 - 详解

2025-10-21 11:05  tlnshuju  阅读(0)  评论(0)    收藏  举报

文章目录

    • 一、基础概念与术语
      • 1.1 几个基本概念
      • 1.2 使用建议
    • 二、哈希(单向加密)
      • 2.1 基本哈希函数 (MD5, SHA-256)
      • 2.2 HMAC (Hash-based Message Authentication Code)
    • 三、对称加密(AES)
      • 3.1 AES-GCM 加密与解密
    • 四、非对称加密(RSA)
      • 4.1 RSA 密钥对生成
      • 4.2 RSA 加密与解密
    • 五、密钥派生函数
      • 5.1 PBKDF2
    • 六、实战案例:安全的配置文件读写

一、基础概念与术语

在当今这个数据安全至关重要的时代,加密是保护敏感信息(如用户密码、个人身份信息、支付数据等)不被未授权访问的核心技术。Go 语言通过其标准库 crypto 提供了丰富且强大的加密功能。crypto 包下包含了多个子包,分别用于实现不同类型的加密算法和工具。

1.1 几个基本概念

在开始编码之前,理解几个基本概念至关重要:

1.2 使用建议

  1. 永远不要自己发明加密算法:使用经过广泛审查和测试的标准算法,如 AES, RSA, SHA-256, PBKDF2。Go 的 crypto 包已经为你做好了这些。
  2. 密钥管理是最大的挑战
    • 不要硬编码密钥:将密钥直接写在源代码中是极其危险的行为。一旦代码泄露,密钥就泄露了。
    • 使用安全的密钥存储:对于生产环境,应使用专门的密钥管理服务,如 HashiCorp Vault, AWS KMS, Google Cloud KMSAzure Key Vault
    • 环境变量:对于简单的应用,可以通过环境变量注入密钥,这比硬编码好,但也不是最安全的方案(环境变量可能被其他进程读取)。
  3. 使用正确的工具做正确的事
    • 存储密码:使用 带盐的强哈希,如 bcrypt (推荐), scryptPBKDF2绝对不要使用可逆加密(如 AES)或普通哈希(如 SHA-256)来存储密码golang.org/x/crypto/bcrypt 是处理密码的最佳选择。
    • 加密数据:使用 AES-GCM 进行对称加密。它既快又安全,还内置了认证。
    • 安全传输:使用 TLS/SSL。这是保护网络通信的标准,不要自己实现传输层加密。
    • 数字签名:使用 RSA-PSSECDSA
  4. 使用安全的随机数生成器:任何需要随机性的地方(如生成 Nonce, IV, Salt),都必须使用 crypto/rand,而不是 math/randmath/rand 是伪随机的,可预测,不适用于安全场景。
  5. 处理错误:加密操作中的错误往往是安全问题的信号。例如,解密失败很可能意味着密钥错误或数据被篡改。务必妥善处理这些错误,不要简单地忽略。
  6. 保持更新:加密领域在不断进步,旧的算法可能会被发现漏洞。关注 Go 官方博客和安全社区的建议,及时更新你的加密实践。

二、哈希(单向加密)

哈希函数不是用来加密解密的,而是用来生成数据的“指纹”。最常见的应用场景是存储用户密码和校验文件完整性。

2.1 基本哈希函数 (MD5, SHA-256)

Go 的 crypto 包提供了多种哈希算法的实现。MD5 和 SHA-1 由于存在安全漏洞,已不推荐用于安全目的,但了解它们仍有意义。SHA-256 是目前最常用的安全哈希算法之一。

案例代码:计算字符串的哈希值

package main
import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"fmt"
)
func main() {
data := []byte("Hello, Go Encryption!")
// MD5 (不安全,仅作演示)
md5Hash := md5.Sum(data)
fmt.Printf("MD5: %x\n", md5Hash)
// SHA-1 (不安全,仅作演示)
sha1Hash := sha1.Sum(data)
fmt.Printf("SHA-1: %x\n", sha1Hash)
// SHA-256 (推荐)
sha256Hash := sha256.Sum256(data)
fmt.Printf("SHA-256: %x\n", sha256Hash)
// SHA-512
sha512Hash := sha512.Sum512(data)
fmt.Printf("SHA-512: %x\n", sha512Hash)
}

输出示例:

MD5: 9e7b5c9e9e5c9e9e5c9e9e5c9e9e5c9e
SHA-1: a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
SHA-256: 2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae
SHA-512: 9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043

注意%x 是一个格式化动词,用于将字节切片以十六进制字符串的形式输出。

2.2 HMAC (Hash-based Message Authentication Code)

HMAC 是一种基于哈希的消息认证码。它需要一个密钥和一个消息作为输入,输出一个固定长度的哈希值。HMAC 不仅可以验证数据的完整性,还可以验证消息的来源(因为只有拥有相同密钥的人才能生成相同的 HMAC)。它比单纯的哈希更安全,可以有效防止“长度扩展攻击”。

案例代码:生成和验证 HMAC

package main
import (
"crypto/hmac"
"crypto/sha256"
"fmt"
)
func main() {
message := []byte("This is a secret message.")
// 在实际应用中,这个密钥应该安全地存储,不能硬编码在代码里
key := []byte("my-super-secret-key-12345")
// --- 生成 HMAC ---
// 创建一个使用 SHA256 算法的 HMAC 实例
h := hmac.New(sha256.New, key)
// 写入数据
h.Write(message)
// 计算最终的 HMAC 值
mac := h.Sum(nil)
fmt.Printf("HMAC-SHA256: %x\n", mac)
// --- 验证 HMAC ---
// 接收方收到 message 和 mac 后,用同样的 key 重新计算一次
receivedMessage := []byte("This is a secret message.")
receivedMac := mac // 假设这是从网络或文件中收到的 MAC
// 创建新的 HMAC 实例进行验证
h2 := hmac.New(sha256.New, key)
h2.Write(receivedMessage)
expectedMac := h2.Sum(nil)
// 使用 hmac.Equal 进行安全比较,防止时序攻击
if hmac.Equal(receivedMac, expectedMac) {
fmt.Println("HMAC verification: SUCCESS! The message is authentic and intact.")
} else {
fmt.Println("HMAC verification: FAILED! The message may have been tampered with.")
}
}

三、对称加密(AES)

AES (Advanced Encryption Standard) 是目前最流行和安全的对称加密标准。Go 的 crypto/aes 包提供了 AES 的实现。
AES 有几种工作模式,如 ECB, CBC, GCM 等。

  • ECB (Electronic Codebook):不安全,因为相同的明文块会生成相同的密文块。切勿使用
  • CBC (Cipher Block Chaining):每个明文块在加密前会与前一个密文块进行异或操作。需要一个初始化向量 来加密第一个块。IV 不需要保密,但必须是不可预测的,通常每次加密都随机生成。
  • GCM (Galois/Counter Mode):是目前推荐的模式。它不仅提供了加密,还提供了认证(像 HMAC 一样),可以同时保证数据的机密性完整性。它比 CBC + HMAC 的组合更高效、更简洁。
    我们将重点介绍 AES-GCM

3.1 AES-GCM 加密与解密

案例代码:使用 AES-GCM 进行加密和解密

package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
)
// GCMEncrypt 使用 AES-GCM 加密明文
// 返回: 密文 (nonce + ciphertext), 错误
func GCMEncrypt(key []byte, plaintext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// 推荐 GCM 模式
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
// Nonce (Number used once) 必须是 GCM.NonceSize() 长度,并且每次加密都应该是唯一的
// 这里我们使用 crypto/rand 生成一个随机的 nonce
nonce := make([]byte, gcm.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
// Seal 会将 nonce 和密文拼接在一起返回
// 格式: nonce + ciphertext + tag
// tag 是用于认证的,GCM 内部会自动处理
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
return ciphertext, nil
}
// GCMDecrypt 使用 AES-GCM 解密密文
// 密文格式: nonce + ciphertext + tag
func GCMDecrypt(key []byte, ciphertext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonceSize := gcm.NonceSize()
if len(ciphertext) < nonceSize {
return nil, fmt.Errorf("ciphertext too short")
}
// 从密文中分离出 nonce
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
// Open 会自动验证 tag,如果验证失败会返回 error
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, err
}
return plaintext, nil
}
func main() {
// AES-256 需要 32 字节的密钥
// 在实际应用中,这个密钥应该从安全的来源获取,绝对不能硬编码!
key, _ := hex.DecodeString("6368616e676520746869732070617373776f726420746f206120736563726574")
plaintext := []byte("This is a top-secret message that must be protected.")
fmt.Printf("Original Plaintext: %s\n", plaintext)
fmt.Printf("Key (hex): %x\n", key)
// --- 加密 ---
ciphertext, err := GCMEncrypt(key, plaintext)
if err != nil {
panic(err)
}
fmt.Printf("Ciphertext (hex): %x\n", ciphertext)
// --- 解密 ---
decryptedPlaintext, err := GCMDecrypt(key, ciphertext)
if err != nil {
panic(err)
}
fmt.Printf("Decrypted Plaintext: %s\n", decryptedPlaintext)
}

四、非对称加密(RSA)

RSA 是最著名的非对称加密算法。它主要用于两个场景:

  1. 加密:用公钥加密,私钥解密。常用于加密少量数据,比如对称加密的密钥。
  2. 数字签名:用私钥签名,公钥验签。用于验证身份和消息的不可否认性。

4.1 RSA 密钥对生成

首先,我们需要生成一对公钥和私钥。
案例代码:生成 RSA 密钥对并保存到文件

package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
)
// generateRSAKeys 生成 RSA 密钥对并保存到文件
func generateRSAKeys(bits int, privateKeyFile, publicKeyFile string) error {
// 1. 生成私钥
privateKey, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return err
}
// 2. 将私钥序列化为 ASN.1 DER 格式
privateKeyDER := x509.MarshalPKCS1PrivateKey(privateKey)
// 3. 创建 PEM 块
privateKeyPEM := &pem.Block{
Type:  "RSA PRIVATE KEY",
Bytes: privateKeyDER,
}
// 4. 将私钥 PEM 块写入文件
privateFile, err := os.Create(privateKeyFile)
if err != nil {
return err
}
defer privateFile.Close()
if err := pem.Encode(privateFile, privateKeyPEM); err != nil {
return err
}
fmt.Printf("Private key saved to %s\n", privateKeyFile)
// 5. 从私钥中提取公钥
publicKey := &privateKey.PublicKey
// 6. 将公钥序列化为 ASN.1 DER 格式
publicKeyDER, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return err
}
// 7. 创建 PEM 块
publicKeyPEM := &pem.Block{
Type:  "PUBLIC KEY",
Bytes: publicKeyDER,
}
// 8. 将公钥 PEM 块写入文件
publicFile, err := os.Create(publicKeyFile)
if err != nil {
return err
}
defer publicFile.Close()
if err := pem.Encode(publicFile, publicKeyPEM); err != nil {
return err
}
fmt.Printf("Public key saved to %s\n", publicKeyFile)
return nil
}
func main() {
// 生成 2048 位的 RSA 密钥对
err := generateRSAKeys(2048, "private.pem", "public.pem")
if err != nil {
fmt.Println("Error generating RSA keys:", err)
}
}

运行此代码后,会生成 private.pempublic.pem 两个文件。

4.2 RSA 加密与解密

RSA 加密的数据长度有限制,与密钥长度有关。例如,2048位的密钥最多只能加密 245 字节的数据。因此,它通常用于加密一个对称密钥,而不是直接加密大文件。
案例代码:使用 RSA 公钥加密,私钥解密

package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
)
// loadPrivateKey 从 PEM 文件加载私钥
func loadPrivateKey(filename string) (*rsa.PrivateKey, error) {
keyBytes, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
block, _ := pem.Decode(keyBytes)
if block == nil {
return nil, fmt.Errorf("failed to decode PEM block containing private key")
}
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return privateKey, nil
}
// loadPublicKey 从 PEM 文件加载公钥
func loadPublicKey(filename string) (*rsa.PublicKey, error) {
keyBytes, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
block, _ := pem.Decode(keyBytes)
if block == nil {
return nil, fmt.Errorf("failed to decode PEM block containing public key")
}
publicKey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
rsaPublicKey, ok := publicKey.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("not an RSA public key")
}
return rsaPublicKey, nil
}
func main() {
// 假设我们已经通过上一个例子生成了密钥对
privateKey, err := loadPrivateKey("private.pem")
if err != nil {
panic(err)
}
publicKey, err := loadPublicKey("public.pem")
if err != nil {
panic(err)
}
plaintext := []byte("This is a secret message for RSA encryption.")
fmt.Printf("Original Plaintext: %s\n", plaintext)
// --- 公钥加密 ---
// OAEP 是比 PKCS#1 v1.5 更安全的填充方案
ciphertext, err := rsa.EncryptOAEP(
sha256.New(),
rand.Reader,
publicKey,
plaintext,
nil, // 可选的标签
)
if err != nil {
panic(err)
}
fmt.Printf("Ciphertext (hex): %x\n", ciphertext)
// --- 私钥解密 ---
decryptedPlaintext, err := rsa.DecryptOAEP(
sha256.New(),
rand.Reader,
privateKey,
ciphertext,
nil, // 可选的标签
)
if err != nil {
panic(err)
}
fmt.Printf("Decrypted Plaintext: %s\n", decryptedPlaintext)
}

注意:为了运行上面的代码,你需要先导入 crypto/sha256 包。

五、密钥派生函数

用户输入的密码通常不够强壮,不能直接用作加密密钥。密钥派生函数可以从一个低熵的密码(可能还加上一个“盐”值)中派生出一个高熵的、适合加密的密钥。

5.1 PBKDF2

PBKDF2 (Password-Based Key Derivation Function 2) 是一个广泛使用的密钥派生函数。它通过多次哈希迭代来增加暴力破解的难度。

案例代码:使用 PBKDF2 从密码派生密钥

package main
import (
"crypto/rand"
"crypto/sha256"
"fmt"
"golang.org/x/crypto/pbkdf2"
)
func main() {
password := []byte("my-weak-password-123")
// 盐值应该是随机的,并且与密文一起存储
salt := make([]byte, 16)
if _, err := rand.Read(salt); err != nil {
panic(err)
}
// 迭代次数,越高越安全,但也越慢
iterations := 100000
// 想要的密钥长度,例如 AES-256 需要 32 字节
keyLength := 32
// 使用 PBKDF2 派生密钥
// 参数: 密码, 盐值, 迭代次数, 密钥长度, 哈希函数
derivedKey := pbkdf2.Key(password, salt, iterations, keyLength, sha256.New)
fmt.Printf("Password: %s\n", password)
fmt.Printf("Salt (hex): %x\n", salt)
fmt.Printf("Iterations: %d\n", iterations)
fmt.Printf("Derived Key (hex): %x\n", derivedKey)
}

注意golang.org/x/crypto/pbkdf2 是 Go 的一个子仓库,提供了额外的加密算法。你需要先通过 go get golang.org/x/crypto/pbkdf2 来安装它。

六、实战案例:安全的配置文件读写

让我们结合 AES-GCMPBKDF2 来创建一个工具,它可以安全地加密一个配置文件,然后再解密它。

场景

  1. 用户有一个 config.json 文件,包含敏感信息。
  2. 用户输入一个密码。
  3. 程序使用密码派生出一个 AES 密钥,然后用这个密钥加密 config.json,生成 config.enc
  4. 程序也能读取 config.enc,用同样的密码解密,还原出 config.json

案例代码:secure_config.go

package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/json"
"fmt"
"golang.org/x/crypto/pbkdf2"
"crypto/sha256"
"io"
"io/ioutil"
"os"
)
// Config 定义我们的配置结构
type Config struct {
DatabaseURL string `json:"database_url"`
APIKey      string `json:"api_key"`
Secret      string `json:"secret"`
}
// deriveKey 从密码和盐值派生 AES 密钥
func deriveKey(password string, salt []byte) []byte {
return pbkdf2.Key([]byte(password), salt, 100000, 32, sha256.New)
}
// encryptFile 加密文件
func encryptFile(filename string, password string) error {
// 1. 读取原始文件
plaintext, err := ioutil.ReadFile(filename)
if err != nil {
return fmt.Errorf("failed to read file: %w", err)
}
// 2. 生成随机盐值
salt := make([]byte, 16)
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
return fmt.Errorf("failed to generate salt: %w", err)
}
// 3. 派生密钥
key := deriveKey(password, salt)
// 4. 创建 AES-GCM 加密器
block, err := aes.NewCipher(key)
if err != nil {
return fmt.Errorf("failed to create cipher: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return fmt.Errorf("failed to create GCM: %w", err)
}
// 5. 生成 Nonce
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return fmt.Errorf("failed to generate nonce: %w", err)
}
// 6. 加密
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
// 7. 将盐值和密文写入新文件 (盐值需要存储以便解密)
// 文件格式: salt + ciphertext
outputFile := filename + ".enc"
err = ioutil.WriteFile(outputFile, append(salt, ciphertext...), 0644)
if err != nil {
return fmt.Errorf("failed to write encrypted file: %w", err)
}
fmt.Printf("File '%s' encrypted successfully to '%s'\n", filename, outputFile)
return nil
}
// decryptFile 解密文件
func decryptFile(encryptedFilename string, password string) error {
// 1. 读取加密文件
data, err := ioutil.ReadFile(encryptedFilename)
if err != nil {
return fmt.Errorf("failed to read encrypted file: %w", err)
}
// 2. 提取盐值和密文
// 我们知道盐值是 16 字节
if len(data) < 16 {
return fmt.Errorf("invalid encrypted file: too short")
}
salt := data[:16]
ciphertext := data[16:]
// 3. 派生密钥 (使用和加密时相同的密码和盐值)
key := deriveKey(password, salt)
// 4. 创建 AES-GCM 解密器
block, err := aes.NewCipher(key)
if err != nil {
return fmt.Errorf("failed to create cipher: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return fmt.Errorf("failed to create GCM: %w", err)
}
nonceSize := gcm.NonceSize()
if len(ciphertext) < nonceSize {
return fmt.Errorf("invalid ciphertext: too short")
}
// 5. 提取 Nonce 和实际密文
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
// 6. 解密
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
// 这个错误通常意味着密码错误或数据被篡改
return fmt.Errorf("decryption failed (wrong password or corrupted file?): %w", err)
}
// 7. 写入解密后的文件
originalFilename := encryptedFilename[:len(encryptedFilename)-4] // 移除 .enc 后缀
err = ioutil.WriteFile(originalFilename, plaintext, 0644)
if err != nil {
return fmt.Errorf("failed to write decrypted file: %w", err)
}
fmt.Printf("File '%s' decrypted successfully to '%s'\n", encryptedFilename, originalFilename)
return nil
}
func main() {
// --- 准备一个测试配置文件 ---
config := Config{
DatabaseURL: "postgres://user:pass@host:5432/db",
APIKey:      "sk-1234567890abcdef",
Secret:      "this-is-a-very-important-secret",
}
configData, _ := json.MarshalIndent(config, "", "  ")
_ = ioutil.WriteFile("config.json", configData, 0644)
fmt.Println("Created a sample config.json file.")
// --- 加密 ---
password := "my-super-strong-password"
err := encryptFile("config.json", password)
if err != nil {
panic(err)
}
// 为了演示,我们删除原始文件
_ = os.Remove("config.json")
// --- 解密 ---
err = decryptFile("config.json.enc", password)
if err != nil {
panic(err)
}
// --- 验证解密后的文件 ---
decryptedConfigData, _ := ioutil.ReadFile("config.json")
fmt.Println("\nDecrypted config content:")
fmt.Println(string(decryptedConfigData))
}

总结:Go 语言的 crypto 包及其子仓库(golang.org/x/crypto)为开发者提供了一套强大、现代且易于使用的加密工具箱。通过本文的学习,你应该已经掌握了:

  • 如何使用哈希(SHA-256)和 HMAC 进行数据摘要和认证。
  • 如何使用 AES-GCM 进行高效且安全的对称加密和解密。
  • 如何生成和使用 RSA 密钥对进行非对称加密。
  • 如何使用 PBKDF2 从用户密码安全地派生加密密钥。
  • 如何将这些技术组合起来,解决实际问题,如安全地存储配置文件。

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

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

相关文章

unity 读取PPT显现到屏幕功能

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025年10月小红书代理商实力榜:五家对比评测与避坑指南

一、引言 小红书月活已突破三亿,日均搜索量同比提升百分之六十以上,对快消、美妆、餐饮、家装等赛道而言,它既是新品首发场,也是用户决策链的关键节点。品牌方、创业者、电商采购者在筛选代理商时,普遍面临三大痛…

设计模式(C++)详解——备忘录模式(1) - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

P1725 琪露诺 解题笔记

原题链接 我会暴力dp! 设 \(dp_i\) 为刚好到第 \(i\) 个格子的最大值。那么 \(dp_i\) 就可以从 \([i - RR, i - LL]\) 这段区间内转移。 则可以得出状态转移方程 \(dp_i = dp_{j} + a_i\) (\(j\) 在 \([i - RR, i - …

【电商行业案例】基于Vaadin全栈Java框架,打造百万级订单的B2B电商SaaS平台

在全球B2B电商领域,意大利SaaS平台 Rewix 正在用全新的方式重塑企业间的数字化交易体验。借助 Vaadin 全栈 Java 框架,Rewix 成功将传统的电商后台系统升级为现代化、可扩展的企业级 SaaS 平台——实现了每家客户百万…

【触想智能】什么是人脸识别一体机,人脸识别一体机主要应用于哪些领域?

人脸识别作为一项崭新的技术,正在迅速改变人们对于安全和便捷的认知。人脸识别技术的广泛应用,推动了人脸识别一体机的兴起。那么,什么是人脸识别一体机,以及这种设备主要应用于哪些领域呢?触想人脸识别一体机在智…

文档智能处理桌面软件开源

一.简介 本次针对「OCR识别」「文档比对」「文档图片处理」「视频转PPT/PDF」四款核心工具完成更新,从稳定性、轻量化、易用性三大维度优化体验,搭配全新名称与图标,让功能匹配更清晰,以下为详细内容。 二、体积压…

使用 LangChain 和 LangGraph 构建一个简单的多智能体系统

# 多智能体报告生成系统 本文介绍了一个简单的多智能体系统的实现,使用 LangChain 结合 LangGraph 来构建这样的系统,LangGraph 作为 LangChain 生态的重要组成部分,专为构建有状态、多智能体的工作流而设计,非常适…

【能源与流程工业案例】KBC借助TeeChart 打造工业级数据可视化平台

在能源与流程工业的数字化转型进程中,数据可视化扮演着至关重要的角色。今天,我们为大家分享KBC如何借助Steema旗下TeeChart的强大图表引擎,成功构建了面向工业级仿真场景的高性能数据可视化平台的案例。在能源与流…

2025年10月上海装修公司口碑榜:千州装饰领衔对比评测排行

一、引言 在上海这样的一线城市,装修支出往往占据家庭总资产的显著比例,创业者、新婚家庭、改善型购房者普遍面临“预算有限、品质难控、工期拖延”三大痛点。为了把试错成本降到最低,用户需要一份基于最新行业动态…

WPF使用MediaCapture开发相机应用(二、相机预览优化)

之前初步做好了相机预览的功能,但回头越想越不对劲,预览的延时感觉有点高了,于是切出系统的相机应用对比了一下:可以看到系统的相机都跑到26.55了,我开发的相机才跑到26.42,慢了一百多毫秒,感觉需要狠狠的优化一…

自己动手做一款ChatExcel数据分析系统,智能分析 Excel 数据

在日常办公、业务分析或是学生处理作业数据时,Excel 表格几乎是大家离不开的数据处理工具。但传统的 Excel 分析往往需要掌握复杂的函数和代码知识,这让不少人在面对大量数据时望而却步。而今天,我要给大家介绍的 C…

ROS-Navigation Move_base 源码阅读学习--恢复行为recovery_behavior(旋转恢复行为、代价地图清理恢复行为) - 教程

ROS-Navigation Move_base 源码阅读学习--恢复行为recovery_behavior(旋转恢复行为、代价地图清理恢复行为) - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; …

2025年10月无缝钢管推荐榜:五强对比评测与采购指南

无缝钢管是能源、化工、锅炉、船舶、机械五大行业的“血管”,项目一旦开工,管材的到货节奏、材质合规性、壁厚均匀度直接决定施工周期和后期安检。2025年国内在建火电、炼化一体化、氢能储运项目同步推进,带动无缝钢…

2025年10月股票开户券商推荐:五大主流平台对比评测榜

一、引言 对于计划进入A股、港股或基金市场的个人投资者而言,选择一家合规稳健、服务成熟、成本可控的券商是迈出投资第一步的关键。开户流程是否顺畅、交易通道是否稳定、后续投顾与产品资源是否丰富,直接影响后续资…

万象EXCEL开发(十)excel 高级混合查询 ——东方仙盟金丹期 - 教程

万象EXCEL开发(十)excel 高级混合查询 ——东方仙盟金丹期 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "…

实用指南:构建AI智能体:五十二、反应式智能体:AI世界的条件反射,真的可以又快又稳

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

redis-(伪)主从集群搭建

redis-(伪)主从集群搭建为了避免Redis的单点故障问题,可以搭建一个redis集群,将数据备份到集群中的其他节点上,如果其中一个 redis节点宕机,则由集群中的其他节点顶上。redis的主从集群是一个“一主多从”的读写…

za3J5cHRvc+WvhueggeWOn+aWhw

dEMUFPHZLRFAXYUSDJKZLDKRNSHGNFIVJYQTQUXQBQVYUVLLTREVJYQTMKYRDMFDVFPJUDEEHZWETZYVGWHKKQETGFQJNCEGGWHKK?DQMCPFQZDQMMIAGPFXHQRLGTIMVMZJANQLVKQEDAGDVFRPJUNGEUNAQZGZLECGYUXUEENJTBJLBQCRTBJDFHRRYIZETKZEMVDU…

结对项目:小学四则运算题目的命令行程序

小学四则运算题目生成器项目报告 一.项目信息项目 内容这个作业属于哪个课程 软件工程这个作业要求在哪里 结对项目这个作业的目标 设计实现小学四则运算题目生成器,支持题目生成、答案计算、重复性检测和自动批改,并…