Web 安全进阶:前端信封加解密技术详解

目录

信封加解密中的核心术语

关键概念关联图

信封加解密核心原理

典型应用场景

具体实现步骤

步骤1:生成DEK(数据加密密钥)

步骤2:用DEK加密数据

步骤3:用KEK加密DEK

步骤4:组装"信封"

步骤5:解密流程

信封加解密全流程示例图​

[前端代码示例(Web Crypto API)](#前端代码示例(Web Crypto API))

信封加解密安全注意事项

与KMS服务集成

[Web Crypto讲解](#Web Crypto讲解)

[Web Crypto 核心特性](#Web Crypto 核心特性)

[Web Crypto 核心对象与方法](#Web Crypto 核心对象与方法)

[Web Crypto 典型使用场景](#Web Crypto 典型使用场景)

[1. 计算文件哈希(如校验文件完整性)](#1. 计算文件哈希(如校验文件完整性))

[2. AES-GCM 加密数据](#2. AES-GCM 加密数据)

[3. 非对称加密(RSA-OAEP)](#3. 非对称加密(RSA-OAEP))

[Web Crypto 密钥管理](#Web Crypto 密钥管理)

[1. 导出/导入密钥(JWK 格式)](#1. 导出/导入密钥(JWK 格式))

[2. 密钥派生(PBKDF2)](#2. 密钥派生(PBKDF2))

[Web Crypto 安全注意事项](#Web Crypto 安全注意事项)

[Web Crypto 与其他库的对比](#Web Crypto 与其他库的对比)

[Web Crypto 浏览器兼容性](#Web Crypto 浏览器兼容性)

[Web Crypto 总结](#Web Crypto 总结)

对称加密和非对称加密的详细对比解析

[对称加密 vs 非对称加密对比表](#对称加密 vs 非对称加密对比表)

对称加密详解

优点

缺点

使用场景

非对称加密详解

优点

缺点

使用场景

核心算法对比

混合加密系统(最佳实践)

策略选择


前端信封加解密(Envelope Encryption)是一种结合对称加密和非对称加密(或分层密钥管理)的技术,主要用于解决密钥管理大规模数据加密的安全性问题。


信封加解密中的核心术语

术语 定义 作用 技术细节 典型应用场景
信封加密 (Envelope Encryption) 一种分层加密技术,通过两层密钥(DEK + KEK)保护数据。 平衡性能与安全性:用对称加密处理大数据,用非对称加密保护密钥。 组合 AES(对称) + RSA/ECC(非对称) 云存储加密、前端敏感数据传输、端到端加密。
DEK (Data Encryption Key) 用于直接加密数据的临时对称密钥。 加密明文数据,保证数据机密性。 算法:AES-GCM、AES-CBC 长度:128/256 位 生命周期:单次使用或短期有效。 加密用户上传的文件、数据库字段加密。
KEK (Key Encryption Key) 用于加密 DEK 的主密钥(对称或非对称)。 保护 DEK,避免直接暴露主密钥。 对称:AES-256 非对称:RSA-2048/ECC-384 生命周期:长期存储,需严格保护。 AWS KMS、阿里云 KMS 中的主密钥管理。
IV (Initialization Vector) 初始化向量,配合对称加密算法使用。 确保相同明文加密后生成不同密文,防止模式攻击。 长度:AES-GCM 为 12 字节 生成方式:随机值(无需保密) AES-GCM/AES-CBC 加密时生成随机 IV。
KMS (Key Management Service) 密钥管理服务,用于生成、存储和管理 KEK。 集中管理密钥,提供安全的密钥存储和访问控制。 支持硬件安全模块(HSM)、密钥轮换、审计日志。 云服务(如 AWS KMS、Google Cloud KMS)中的密钥托管。
RSA-OAEP 一种非对称加密算法(Optimal Asymmetric Encryption Padding)。 安全加密 DEK,防止选择密文攻击。 填充模式:OAEP 哈希算法:SHA-256 密钥长度:2048 位以上。 加密 DEK 并传输给后端。
AES-GCM 对称加密算法(Galois/Counter Mode)。 高效加密数据,同时提供完整性和认证。 密钥长度:128/256 位 附加认证数据(AAD):可选 输出:密文 + 认证标签(Authentication Tag)。 加密用户敏感信息(如身份证号、银行卡号)。
密钥派生 (Key Derivation) 从主密钥或密码派生子密钥的过程。 减少密钥存储数量,增强密钥复用安全性。 算法:PBKDF2、HKDF、Scrypt 参数:盐值(Salt)、迭代次数。 从用户密码派生加密密钥。
密钥轮换 (Key Rotation) 定期更换密钥的策略。 降低密钥泄露风险,满足合规要求。 自动轮换周期(如 90 天) 历史密钥保留策略。 云服务中的 KEK 自动轮换。
密文 (Ciphertext) 加密后的数据。 保护明文数据,防止未授权访问。 格式:Base64 或二进制 包含元数据:IV、认证标签(如 AES-GCM)。 存储在数据库或传输中的加密结果。
明文 (Plaintext) 未加密的原始数据。 加密前的可读数据。 文本、二进制文件等。 用户输入的密码、身份证号等敏感信息。
密钥包装 (Key Wrapping) 用 KEK 加密 DEK 的过程。 安全传输/存储 DEK。 API:crypto.subtle.wrapKey() 格式:RAW/PKCS8/JWK。 前端用 RSA 公钥加密 DEK 后传输给后端。
密钥解包 (Key Unwrapping) 用 KEK 解密 DEK 的过程。 恢复 DEK 以解密数据。 API:crypto.subtle.unwrapKey() 需指定目标算法和用途。 后端用 RSA 私钥解密 DEK 后解密数据。
端到端加密 (E2EE) 数据仅在发送方和接收方解密,中间节点无法访问明文。 保护通信隐私,防止中间人窃听。 结合信封加密与密钥协商协议(如 Diffie-Hellman)。 即时通讯(如 WhatsApp)、安全文件传输。
密钥生命周期管理 密钥从生成到销毁的全过程管理。 确保密钥安全性和合规性。 阶段:生成 -> 激活 -> 轮换 -> 归档 -> 销毁。 企业级 KMS 中的密钥策略配置。

关键概念关联图

javascript 复制代码
           +-----------------+
           |      KEK        | (主密钥,长期存储)
           +--------+--------+
                    |
                    | 加密/解密
                    v
           +-----------------+
           |      DEK        | (临时密钥,加密数据)
           +--------+--------+
                    |
                    | 加密/解密
                    v
           +-----------------+
           |   Plaintext     | <--> |    Ciphertext    |
           +-----------------+     +------------------+

信封加解密核心原理

信封加密的核心思想是 "用密钥加密密钥",分为两层加密:

  1. 数据加密密钥(DEK, Data Encryption Key)

    • 使用对称加密算法(如AES)加密实际数据,速度快,适合大数据量。

    • DEK是临时生成的随机密钥,每次加密时动态创建。

  2. 密钥加密密钥(KEK, Key Encryption Key)

    • 使用非对称加密算法(如RSA)或另一个对称密钥加密DEK。

    • KEK是长期存储的主密钥,需严格保护(如存储在硬件安全模块HSM或KMS中)。

最终加密结果称为"信封",包含:

  • 密文(数据加密后的结果)

  • 加密后的DEK(用KEK加密后的DEK)


典型应用场景

  1. 云端数据加密

    如AWS KMS、阿里云KMS,用户通过KEK管理DEK,避免主密钥频繁暴露。

  2. 前端敏感数据加密

    前端加密用户数据后传输到后端,后端仅存储加密后的DEK和密文。

  3. 端到端加密(E2EE)

    结合信封加密和密钥协商协议(如Diffie-Hellman),实现通信双方的安全数据传输。

具体实现步骤

前端加密数据为例,典型流程如下:

步骤1:生成DEK(数据加密密钥)

javascript 复制代码
// 使用Web Crypto API生成随机AES密钥
const dek = await crypto.subtle.generateKey(
  { name: "AES-GCM", length: 256 },
  true, // 可导出(若需要)
  ["encrypt", "decrypt"]
);

步骤2:用DEK加密数据

javascript 复制代码
const data = new TextEncoder().encode("敏感数据");
const iv = crypto.getRandomValues(new Uint8Array(12)); // 初始化向量
const encryptedData = await crypto.subtle.encrypt(
  { name: "AES-GCM", iv },
  dek,
  data
);

步骤3:用KEK加密DEK

假设KEK是一个预先配置的公钥(非对称加密):

javascript 复制代码
// 假设kekPublicKey是后端提供的RSA公钥
const encryptedDEK = await crypto.subtle.wrapKey(
  "raw", // 导出格式
  dek,
  kekPublicKey,
  { name: "RSA-OAEP" }
);

步骤4:组装"信封"

将加密后的数据和加密后的DEK组合发送到后端:

javascript 复制代码
{
  "encryptedData": "Base64密文",
  "encryptedDEK": "Base64加密后的DEK",
  "iv": "Base64初始化向量"
}

步骤5:解密流程

后端用KEK私钥解密DEK,再用DEK解密数据。

信封加解密全流程示例图

前端代码示例(Web Crypto API)

1.效果预览(仅展示,实际开发请看安全提示哦!)

2.前端展示信封加解密全代码

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <title>信封加密完整示例</title>
    <meta charset="UTF-8">
</head>
<body>
<h2>信封加密演示</h2>
<div style="max-width: 800px; margin: 20px auto;">
    <textarea id="plaintext" rows="4" cols="50" placeholder="输入要加密的文本..."></textarea>
    <div style="margin: 10px 0;">
        <button onclick="encryptData()">加密</button>
        <button onclick="decryptData()">解密</button>
    </div>
    <div>
        <h4>加密结果:</h4>
        <pre id="encryptedResult"></pre>
        <h4>解密结果:</h4>
        <pre id="decryptedResult"></pre>
    </div>
</div>

<script>
    // 存储加密信封的全局对象
    let encryptionEnvelope = null;

    // 修复点1:正确配置密钥用途
    async function generateRSAKeys() {
        /*
        生成RSA密钥对:
        - 公钥用途:wrapKey(加密DEK)
        - 私钥用途:unwrapKey(解密DEK)
        */
        return await crypto.subtle.generateKey(
            {
                name: "RSA-OAEP",
                modulusLength: 2048,
                publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // 65537
                hash: "SHA-256"
            },
            true, // 密钥可导出(用于演示)
            ["wrapKey", "unwrapKey"] // 关键修复:明确密钥用途
        );
    }

    // 加密函数
    async function encryptData() {
        try {
            const plaintext = document.getElementById('plaintext').value;
            if (!plaintext) return alert("请输入要加密的内容");

            // 生成RSA密钥对(KEK)
            const { publicKey, privateKey } = await generateRSAKeys();

            // 生成AES-GCM密钥(DEK)
            const dek = await crypto.subtle.generateKey(
                { name: "AES-GCM", length: 256 },
                true, // 必须为true才能导出
                ["encrypt", "decrypt"] // DEK用途
            );

            // 使用DEK加密数据
            const iv = crypto.getRandomValues(new Uint8Array(12));
            const encryptedData = await crypto.subtle.encrypt(
                { name: "AES-GCM", iv },
                dek,
                new TextEncoder().encode(plaintext)
            );

            // 修复点2:使用正确的wrapKey方法
            const wrappedDek = await crypto.subtle.wrapKey(
                "raw", // 导出格式
                dek,   // 要加密的DEK
                publicKey, // 使用RSA公钥加密
                { name: "RSA-OAEP" }
            );

            // 存储信封数据(仅用于演示)
            encryptionEnvelope = {
                encryptedData,
                wrappedDek,
                iv,
                privateKey // 警告:实际生产环境私钥不能存储在前端!
            };

            // 显示加密结果
            document.getElementById('encryptedResult').textContent =
                `加密数据:${arrayBufferToBase64(encryptedData)}\n` +
                `加密后的DEK:${arrayBufferToBase64(wrappedDek)}\n` +
                `IV:${arrayBufferToBase64(iv)}`;

        } catch (error) {
            handleError("加密失败", error);
        }
    }

    // 解密函数
    async function decryptData() {
        if (!encryptionEnvelope) return alert("请先加密数据");

        try {
            // 修复点3:正确的unwrapKey参数
            const unwrappedDek = await crypto.subtle.unwrapKey(
                "raw", // 导入格式
                encryptionEnvelope.wrappedDek,
                encryptionEnvelope.privateKey,
                {
                    name: "RSA-OAEP",
                    hash: "SHA-256" // 必须与生成时一致
                },
                {
                    name: "AES-GCM", // 必须匹配原始算法
                    length: 256      // 必须指定长度
                },
                true, // 密钥可导出
                ["decrypt"] // 明确解密用途
            );

            // 使用DEK解密数据
            const decrypted = await crypto.subtle.decrypt(
                { name: "AES-GCM", iv: encryptionEnvelope.iv },
                unwrappedDek,
                encryptionEnvelope.encryptedData
            );

            document.getElementById('decryptedResult').textContent =
                new TextDecoder().decode(decrypted);

        } catch (error) {
            handleError("解密失败", error);
        }
    }

    // 工具函数:ArrayBuffer转Base64
    function arrayBufferToBase64(buffer) {
        return btoa(String.fromCharCode(...new Uint8Array(buffer)));
    }

    // 错误处理
    function handleError(context, error) {
        console.error(`${context}:`, error);
        alert(`${context}:${error.message}`);
    }
</script>

<!-- 安全警告 -->
<div style="color: #d32f2f; margin-top: 20px; padding: 15px; background: #ffebee;">
    <strong>安全提示:</strong>
    <ul>
        <li>此示例仅用于论坛学习交流目的,实际生产环境中:
            <ul>
                <li>RSA私钥必须存储在后端安全系统(如HSM/KMS)</li>
                <li>必须通过HTTPS协议传输加密数据</li>
                <li>应定期轮换主密钥(KEK)</li>
            </ul>
        </li>
        <li>前端加密不能替代传输层安全(必须使用HTTPS)</li>
    </ul>
</div>

</body>
</html>

信封加解密安全注意事项

  1. 密钥管理

    KEK必须严格保护,推荐使用HSM或KMS服务,避免硬编码在前端。

  2. 算法选择

    使用AES-GCM(对称)和RSA-OAEP(非对称)等现代算法,避免ECB模式。

  3. 初始化向量(IV)

    IV需随机生成且无需保密,但同一DEK不能重复使用IV。

  4. 前端局限性

    纯前端加密无法完全抵御中间人攻击,需配合HTTPS、证书绑定(Certificate Pinning)等。


与KMS服务集成

云服务(如AWS KMS)的信封加密流程:

  1. 前端请求KMS生成DEK。

  2. KMS返回明文DEK和加密后的DEK。

  3. 前端用明文DEK加密数据,丢弃明文DEK。

  4. 存储加密后的DEK和密文,解密时需调用KMS解密DEK。

Web Crypto讲解

Web Crypto API 是浏览器原生提供的密码学操作接口,用于在客户端(如浏览器)执行加密、解密、签名、哈希等安全操作。它是 W3C 标准的一部分,旨在提供高性能、安全且标准化的密码学功能,取代传统的 JavaScript 加密库(如 CryptoJS)

Web Crypto核心特性

  1. 标准化与安全性

    • 由 W3C 定义,浏览器原生实现,避免第三方库潜在的安全漏洞。

    • 运行在浏览器安全沙箱中,密钥材料无法被 JavaScript 直接访问(需通过 CryptoKey 对象管理)。

  2. 支持主流算法

    • 对称加密:AES(CBC、CTR、GCM 等模式)。

    • 非对称加密:RSA(OAEP、PKCS1-v1.5)、ECC(ECDH、ECDSA)。

    • 哈希算法:SHA-1、SHA-256、SHA-384、SHA-512。

    • 密钥派生:PBKDF2、HKDF。

    • 签名与验证:HMAC、RSA-PSS、ECDSA。

  3. 异步操作

    • 所有方法均返回 Promise,适合处理耗时操作。
  4. 安全上下文限制

    • 仅能在 HTTPSlocalhost 等安全上下文中使用。

Web Crypto核心对象与方法

Web Crypto API 的主入口是 crypto.subtle 对象(SubtleCrypto 接口),提供以下核心方法:

方法 用途
generateKey() 生成密钥(对称或非对称)。
encrypt() / decrypt() 加密/解密数据。
sign() / verify() 生成签名/验证签名。
digest() 计算数据哈希值(如 SHA-256)。
deriveKey() 从基础密钥派生出新密钥(如 PBKDF2)。
wrapKey() / unwrapKey() 包装(加密)/解包(解密)密钥。
importKey() / exportKey() 导入/导出密钥(如从 JWK 格式转换)。

Web Crypto典型使用场景

1. 计算文件哈希(如校验文件完整性)

javascript 复制代码
async function calculateFileHash(file) {
  const buffer = await file.arrayBuffer();
  const hash = await crypto.subtle.digest("SHA-256", buffer);
  return Array.from(new Uint8Array(hash))
    .map(b => b.toString(16).padStart(2, "0"))
    .join("");
}

// 使用示例
const fileInput = document.querySelector("input[type=file]");
fileInput.addEventListener("change", async (e) => {
  const file = e.target.files[0];
  const hash = await calculateFileHash(file);
  console.log("SHA-256:", hash);
});

2. AES-GCM 加密数据

javascript 复制代码
async function encryptData(plainText, key) {
  const encoded = new TextEncoder().encode(plainText);
  const iv = crypto.getRandomValues(new Uint8Array(12)); // 初始化向量
  const encrypted = await crypto.subtle.encrypt(
    { name: "AES-GCM", iv },
    key,
    encoded
  );
  return { encrypted, iv };
}

// 生成 AES 密钥
const key = await crypto.subtle.generateKey(
  { name: "AES-GCM", length: 256 },
  true, // 是否可导出
  ["encrypt", "decrypt"]
);

3. 非对称加密(RSA-OAEP)

javascript 复制代码
// 生成 RSA 密钥对
const { publicKey, privateKey } = await crypto.subtle.generateKey(
  {
    name: "RSA-OAEP",
    modulusLength: 2048,
    publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
    hash: "SHA-256",
  },
  true,
  ["encrypt", "decrypt"]
);

// 加密数据
const encrypted = await crypto.subtle.encrypt(
  { name: "RSA-OAEP" },
  publicKey,
  new TextEncoder().encode("Secret Message")
);

// 解密数据
const decrypted = await crypto.subtle.decrypt(
  { name: "RSA-OAEP" },
  privateKey,
  encrypted
);
console.log(new TextDecoder().decode(decrypted)); // "Secret Message"

Web Crypto密钥管理

1. 导出/导入密钥(JWK 格式)

javascript 复制代码
// 导出公钥为 JWK 格式
const jwkPublicKey = await crypto.subtle.exportKey("jwk", publicKey);

// 导入 JWK 格式密钥
const importedKey = await crypto.subtle.importKey(
  "jwk",
  jwkPublicKey,
  { name: "RSA-OAEP", hash: "SHA-256" },
  true,
  ["encrypt"]
);

2. 密钥派生(PBKDF2)

javascript 复制代码
const password = "user-password";
const salt = crypto.getRandomValues(new Uint8Array(16));

// 从密码派生 AES 密钥
const baseKey = await crypto.subtle.importKey(
  "raw",
  new TextEncoder().encode(password),
  { name: "PBKDF2" },
  false,
  ["deriveKey"]
);

const derivedKey = await crypto.subtle.deriveKey(
  {
    name: "PBKDF2",
    salt,
    iterations: 100000,
    hash: "SHA-256",
  },
  baseKey,
  { name: "AES-GCM", length: 256 },
  true,
  ["encrypt", "decrypt"]
);

Web Crypto安全注意事项

  1. 密钥保护

    • 避免硬编码密钥,优先使用密钥管理系统(如 KMS)或安全存储。
  2. 算法选择

    • 使用现代算法(如 AES-GCM、RSA-OAEP、SHA-256),避免已弃用算法(如 MD5、SHA-1)。
  3. 初始化向量(IV)

    • IV 必须唯一且随机,同一密钥下不可重复使用。
  4. 错误处理

    • 加密操作可能抛出异常(如密钥不匹配),需用 try/catch 捕获。

Web Crypto与其他库的对比

特性 Web Crypto API CryptoJS
实现方式 浏览器原生支持 第三方 JavaScript 库
性能 更高(底层优化) 较低(纯 JS 实现)
安全性 密钥不可直接访问 密钥可能暴露给 JS 代码
算法支持 支持现代算法(如 AES-GCM) 支持传统算法(如 AES-CBC)
使用场景 需安全上下文的 Web 应用 旧浏览器或非安全环境

Web Crypto浏览器兼容性

  • 现代浏览器:Chrome 37+、Firefox 34+、Edge 79+、Safari 11+ 均支持。

  • Polyfill:不推荐(无法实现真正的安全性),优先要求用户升级浏览器。


Web Crypto总结

Web Crypto API 是浏览器端安全操作的黄金标准,适合以下场景:

  • 客户端加密敏感数据(如密码、个人信息)。

  • 文件哈希校验、数字签名。

  • 端到端加密(E2EE)应用的开发。

  • 与后端配合实现信封加密(Envelope Encryption)。

通过合理使用 Web Crypto API,开发者可以在不依赖后端的情况下,提升前端应用的安全性,同时避免传统 JS 加密库的潜在风险。

对称加密和非对称加密的详细对比解析

对称加密 vs 非对称加密对比表

特性 对称加密 非对称加密
定义 使用相同密钥进行加密和解密 使用公钥加密、私钥解密(或私钥签名、公钥验证)
密钥数量 单一密钥 一对密钥(公钥 + 私钥)
典型算法 AES、DES、3DES、ChaCha20 RSA、ECC(椭圆曲线)、ElGamal、DSA
速度 ⚡ 快(适合大数据量) 🐢 慢(比对称加密慢100-1000倍)
密钥管理 🔑 密钥分发困难(需安全通道传输) 🔒 公钥可公开,私钥保密(无需传输私钥)
安全性基础 密钥的保密性 数学难题(如大数分解、椭圆曲线离散对数)
主要用途 数据加密 密钥交换、数字签名、身份认证

对称加密详解

优点

  1. 高性能:加密/解密速度快(AES可达数GB/s)

  2. 资源消耗低:适合IoT设备、移动端等资源受限场景

  3. 算法成熟:AES-GCM等通过NIST认证,安全性高

缺点

  1. 密钥分发难题:需要安全通道传输密钥

  2. 密钥管理复杂:每对通信需要独立密钥(N²增长)

  3. 无身份验证:需配合其他机制实现完整性和认证

使用场景

  • 数据库字段加密(如信用卡号)

  • HTTPS连接的数据加密阶段

  • 文件加密(ZIP/RAR压缩包密码)

  • 磁盘加密(BitLocker、LUKS)


非对称加密详解

优点

  1. 安全密钥交换:通过公钥加密实现安全通信(如RSA-KEM)

  2. 数字签名:可验证数据来源和完整性(如SSL证书)

  3. 身份认证:私钥持有者身份可被验证(如SSH登录)

缺点

  1. 性能瓶颈:不适合加密大数据(通常只用于密钥/小数据)

  2. 密钥长度长:同等安全强度下密钥比对称加密长(RSA-2048 ≈ AES-128)

  3. 实现复杂:易因参数误用导致漏洞(如选择错误的填充模式)

使用场景

  • SSL/TLS握手中的密钥协商

  • 数字签名(代码签名、文档签名)

  • 加密货币钱包地址生成

  • PGP电子邮件加密

  • SSH免密登录认证

核心算法对比

算法 类型 密钥长度 典型应用场景
AES-256-GCM 对称加密 256位 加密数据库内容、HTTPS数据传输
ChaCha20-Poly1305 对称加密 256位 移动端加密(替代AES)、QUIC协议
RSA-2048-OAEP 非对称加密 2048位(等效112位安全) 加密对称密钥、数字证书签发
ECC-secp256k1 非对称加密 256位(等效3072位RSA) 比特币地址生成、物联网设备认证
EdDSA (Ed25519) 非对称签名 256位 SSH密钥对、区块链交易签名

混合加密系统(最佳实践)

实际应用中常结合两者优势:

密钥交换阶段 :用非对称加密安全传输对称密钥

**数据加密阶段:**用对称加密处理实际数据(如HTTPS中的AES)

**数字签名:**用非对称加密验证数据来源(如SSL证书链)

策略选择

需求 推荐方案
加密大量数据 AES-256-GCM + HKDF密钥派生
安全传输密钥 RSA-3072-OAEP 或 ECDH + X25519
数字签名 ECDSA with SHA-384 或 EdDSA
资源受限设备 ChaCha20-Poly1305 或 AES-128-CTR
长期数据存储 AES-256 + 定期密钥轮换策略
相关推荐
总是难免2 分钟前
设计模式 - 模板方法模式
java·设计模式·模板方法模式
yz-俞祥胜20 分钟前
【疑难杂症】Vue前端下载文件无法打开 已解决
前端·javascript·vue.js
TE-茶叶蛋24 分钟前
前端错误监听与上报框架工作原理,如:Sentry
前端·javascript·sentry
漫谈网络30 分钟前
TypeScript 和 JavaScript核心关系及区别
前端·javascript·typescript
aiweker39 分钟前
python web 开发-Flask-Login使用详解
前端·python·flask
m0_679927201 小时前
练习小项目7:天气状态切换器
前端·javascript·css·html
OK_boom1 小时前
React-改变当前页class默认的样式
前端·javascript·react.js
ivwdcwso1 小时前
构建安全与合规的Jenkins环境:全周期审计方案详解
运维·安全·ci/cd·jenkins·devops·审计
哎呦你好2 小时前
【background】CSS 背景全解析:从基础属性到视觉魔法
前端·css·人工智能·python
yuanyxh2 小时前
commonmark.js 源码阅读(二) - Inline Parser
前端·javascript·html