目录

MIME 类型

类型描述典型案例
text表明文件是普通文本text/plain, text/html, text/css, text/javascript
application二进制数据application/octet-stream, application/pkcs12, application/vnd.mspowerpoint, application/xhtml+xml, application/xml, application/pdf
image图像,不包括视频image/gif, image/png, image/jpeg, image/bmp, image/webp, image/x-icon, image/vnd.microsoft.icon
audio音频文件audio/midi, audio/mpeg, audio/webm, audio/ogg, audio/wav
video视频文件video/web,, video/ogg

MIME对照表

媒体类型文件扩展名说明
text/javascriptjs表示 Javascript 脚本文件
text/csscss表示 CSS 样式表
text/htmlhtm, html, shtmlHTML 文件格式

请求头

Header解释示例
Accept-Language浏览器可接受的语言Accept-Language: en,zh
Accept-Charset浏览器可以接受的字符编码集。Accept-Charset: iso-8859-5
Accept-Encoding指定浏览器可以支持的web服务器返回内容压缩编码类型。Accept-Encoding: compress, gzip
Accept-Language浏览器可接受的语言Accept-Language: en,zh
Accept-Ranges可以请求网页实体的一个或者多个子范围字段Accept-Ranges: bytes
AuthorizationHTTP授权的授权证书Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Cache-Control指定请求和响应遵循的缓存机制Cache-Control: no-cache
Connection表示是否需要持久连接。(HTTP 1.1默认进行持久连接)Connection: close
CookieHTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器。Cookie: $Version=1; Skin=new;
Content-Length请求的内容长度Content-Length: 348
Content-Type指定请求体的媒体类型(MIME类型)Content-Type: application/x-www-form-urlencoded
Date请求发送的日期和时间Date: Tue, 15 Nov 2010 08:12:31 GMT
Expect请求的特定的服务器行为Expect: 100-continue
From发出请求的用户的EmailFrom: user@email.com
Host指定请求的服务器的域名和端口号Host: www.zcmhi.com
If-Match只有请求内容与实体相匹配才有效If-Match: “737060cd8c284d8af7ad3082f209582d”
If-Modified-Since如果请求的部分在指定时间之后被修改则请求成功,未被修改则返回304代码If-Modified-Since: Sat, 29 Oct 2010 19:43:31 GMT
If-None-Match如果内容未改变返回304代码,参数为服务器先前发送的Etag,与服务器回应的Etag比较判断是否改变If-None-Match: “737060cd8c284d8af7ad3082f209582d”
If-Range如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。参数也为EtagIf-Range: “737060cd8c284d8af7ad3082f209582d”
If-Unmodified-Since只在实体在指定时间之后未被修改才请求成功If-Unmodified-Since: Sat, 29 Oct 2010 19:43:31 GMT
Max-Forwards限制信息通过代理和网关传送的时间Max-Forwards: 10
Pragma用来包含实现特定的指令Pragma: no-cache
Proxy-Authorization连接到代理的授权证书Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Range只请求实体的一部分,指定范围Range: bytes=500-999
Referer先前网页的地址,当前请求网页紧随其后,即来路Referer: http://www.zcmhi.com/archives/71.html
TE客户端愿意接受的传输编码,并通知服务器接受接受尾加头信息TE: trailers,deflate;q=0.5
Upgrade向服务器指定某种传输协议以便服务器进行转换(如果支持)Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11
User-AgentUser-Agent的内容包含发出请求的用户信息User-Agent: Mozilla/5.0 (Linux; X11)
Via通知中间网关或代理服务器地址,通信协议Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)
Warning关于消息实体的警告信息Warn: 199 Miscellaneous warning

响应头

Header解释示例
Accept-Ranges表明服务器是否支持指定范围请求及哪种类型的分段请求Accept-Ranges: bytes
Age从原始服务器到代理缓存形成的估算时间(以秒计,非负)Age: 12
Allow对某网络资源的有效的请求行为,不允许则返回405Allow: GET, HEAD
Cache-Control告诉所有的缓存机制是否可以缓存及哪种类型Cache-Control: no-cache
Content-Encodingweb服务器支持的返回内容压缩编码类型。Content-Encoding: gzip
Content-Language响应体的语言Content-Language: en,zh
Content-Length响应体的长度Content-Length: 348
Content-Location请求资源可替代的备用的另一地址Content-Location: /index.htm
Content-MD5返回资源的MD5校验值Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ==
Content-Range在整个返回体中本部分的字节位置Content-Range: bytes 21010-47021/47022
Content-Type返回内容的MIME类型Content-Type: text/html; charset=utf-8
Date原始服务器消息发出的时间Date: Tue, 15 Nov 2010 08:12:31 GMT
ETag请求变量的实体标签的当前值ETag: “737060cd8c284d8af7ad3082f209582d”
Expires响应过期的日期和时间Expires: Thu, 01 Dec 2010 16:00:00 GMT
Last-Modified请求资源的最后修改时间Last-Modified: Tue, 15 Nov 2010 12:45:26 GMT
Location用来重定向接收方到非请求URL的位置来完成请求或标识新的资源Location: http://www.zcmhi.com/archives/94.html
Pragma包括实现特定的指令,它可应用到响应链上的任何接收方Pragma: no-cache
Proxy-Authenticate它指出认证方案和可应用到代理的该URL上的参数Proxy-Authenticate: Basic
refresh应用于重定向或一个新的资源被创造,在5秒之后重定向(由网景提出,被大部分浏览器支持)Refresh: 5; url=http://www.zcmhi.com/archives/94.html
Retry-After如果实体暂时不可取,通知客户端在指定时间之后再次尝试Retry-After: 120
Serverweb服务器软件名称Server: Apache/1.3.27 (Unix) (Red-Hat/Linux)
Set-Cookie设置Http CookieSet-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1
Trailer指出头域在分块传输编码的尾部存在Trailer: Max-Forwards
Transfer-Encoding文件传输编码Transfer-Encoding:chunked
Vary告诉下游代理是使用缓存响应还是从原始服务器请求Vary: *
Via告知代理客户端响应是通过哪里发送的Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)
Warning警告实体可能存在的问题Warning: 199 Miscellaneous warning
WWW-Authenticate表明客户端请求实体应该使用的授权方案WWW-Authenticate: Basic

HTTP通用头部参数对照表

头部字段描述示例
Date表示消息创建的日期和时间。“Date: Mon, 23 Jul 2023 15:30:00 GMT”
Cache-Control用于指定缓存行为。“Cache-Control: no-cache, no-store, must-revalidate”
Pragma用于实现向后兼容。“Pragma: no-cache”
Connection用于指定是否使用持久连接。“Connection: keep-alive”
Transfer-Encoding指定了消息体的传输编码方式。“Transfer-Encoding: chunked”
Upgrade表示客户端希望升级到其他协议。“Upgrade: websocket”
Via表示代理服务器及其协议版本。“Via: 1.1 ChromeProxy”

详细解释

http 请求方式

fetch('/download', {
  method: 'POST',
  body: JSON.stringify({ startDate: "2024-01-01", format: "PDF" }),
  headers: { 'Content-Type': 'application/json' }
}).then(response => response.blob().then(blob => {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'report.pdf';
  a.click();
}));
ℹ️note
  • Content-Type: application/octet-stream 声明响应体是二进制流,浏览器默认会尝试下载(即使没有 Content-Disposition)。优先级低 强制所有浏览器将响应视为二进制文件。
  • Content-Disposition: attachment 明确指令要求浏览器下载文件(无论 Content-Type 是什么)。 优先级高 需要自定义文件名或覆盖预览行为。
  • 推荐同时使用二者:
Content-Type: application/octet-stream  # 确保二进制处理
Content-Disposition: attachment; filename="custom-name.ext"  # 确保下载和文件名
@GetMapping("/download/file")
public ResponseEntity<Resource> downloadFile() throws IOException {
    // 1. 读取文件
    Path filePath = Paths.get("/data/reports/report.pdf");
    Resource resource = new FileSystemResource(filePath);

    // 2. 设置响应头
    return ResponseEntity.ok()
            .header(HttpHeaders.CONTENT_DISPOSITION, 
                    "attachment; filename=\"" + filePath.getFileName() + "\"")
            .header(HttpHeaders.CONTENT_TYPE, Files.probeContentType(filePath))
            .header(HttpHeaders.CONTENT_LENGTH, String.valueOf(Files.size(filePath)))
            .header(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate")
            .body(resource);
}
// 如果文件名包含中文或特殊字符,需要额外处理:
String encodedFileName = URLEncoder.encode("中文文件.pdf", StandardCharsets.UTF_8)
        .replaceAll("\\+", "%20"); // 替换空格编码
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName);

URL 编码

十进制码值对应编码名称
950繁体中文
65001UTF-8代码页
936简体中文默认的GBK
437MS-DOS 美国英语

HTPS

❗️important
  • 只有 真正的 CA 才能签发出被信任的证书
  • 如果中间人给客户端发送证书,浏览器会提示,触发用户主动点击继续,那么就是用户自动承担风险

整个HTTPS过程

阶段 1:客户端发起连接 → ClientHello,client_random:用于后续密钥派生
Client → Server:
  - 支持的 TLS 版本(如 TLS 1.2)
  - client_random(32 字节随机数,明文)
  - 支持的加密套件列表:
      TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
      TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
      ...
  - 支持的压缩方法
  - Server Name Indication (SNI): 192.168.66.162(可选)
阶段 2:服务器响应 → ServerHello 到 ServerHelloDone\
Server → Client:
  - 选定的 TLS 版本(如 TLS 1.2)
  - server_random(32 字节随机数,明文)
  - 选定的加密套件:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  - 选定的压缩方法
Server → Client:
  - 发送自己的证书链(你的 `tomcat.keystore` 中的证书)
    包含:
      - 主体:CN=zhumengyu, IP=192.168.66.162
      - 公钥(RSA 公钥)
      - 签名算法
      - 扩展:SAN=IP:192.168.66.162
Server → Client:
  - 椭圆曲线参数(如 secp256r1)
  - 服务器的临时公钥(ECDHE 公钥)
  - 签名:用服务器的 RSA 私钥对(曲线参数 + 临时公钥)签名
Client → Server 证书(用于双向认证 mTLS)
Server → Client: “我的消息发完了”
阶段 3:客户端响应
Client → Server:
  - 发送客户端的临时 ECDHE 公钥
Client → Server: “从现在开始,我用加密通信”
Client → Server:
  - 发送一条“加密的验证消息”
  - 内容是:对之前所有握手消息的哈希(HMAC)
  - 使用刚刚派生的密钥加密
阶段 4:服务器完成握手
Server → Client: “我也开始加密通信”
Server → Client:
  - 发送加密的验证消息(对所有握手消息的哈希)
  - 使用派生的密钥加密

阶段 5:加密应用数据传输

Client → Server:
  - 加密的 HTTP 请求(如 GET /app/login)
  - 使用 AES-GCM 加密
  - 每条消息有 MAC(GCM 模式自带认证)
Server → Client:
  - 加密的响应(如 200 OK + HTML)
密钥派生全过程(关键!)
pre_master_secret = ECDHE_shared_secret (K)

master_secret = PRF(pre_master_secret, "master secret",
                    client_random + server_random)
                [48 bytes]

key_block = PRF(master_secret, "key expansion",
                 server_random + client_random)
ℹ️RSA vs. ECDHE
  • RSA密钥交换详细步骤(没有前向安全:私钥泄露可以得知历史消息)

    • 服务器在 Certificate 中发送自己的 RSA 公钥
    • 客户端生成一个 48 字节的随机数 作为 pre_master_secret
    • 客户端用服务器的 公钥 加密这个 pre_master_secret
    • 发送加密后的数据给服务器
    • 服务器用自己的 私钥 解密,得到 pre_master_secret
    • 双方用 pre_master_secret + client_random + server_random 派生出主密钥
  • ECDHE详细步骤

    • 服务器生成一个 临时的 ECDHE 密钥对
      • 临时私钥(不存储)
      • 临时公钥
    • 在 ServerKeyExchange 中发送临时公钥,并用 RSA 私钥 对其签名
    • 客户端验证签名(证明是服务器发的)
    • 客户端也生成一个 临时的 ECDHE 密钥对
    • 客户端用自己的 临时私钥 和服务器的 临时公钥 计算出 pre_master_secret
    • 客户端发送自己的 临时公钥 给服务器
    • 服务器用自己的 临时私钥 和客户端的 临时公钥 计算出相同的pre_master_secret
    • 双方派生出主密钥

HTTP请求问题

private static CloseableHttpClient createHttpClient() {
    SSLContext sslContext = null;
    try {
        // 1. 创建一个信任所有证书的 SSLContext
        sslContext = SSLContextBuilder.create().loadTrustMaterial(null, new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] chain, String authType) {
                return true; // 信任所有证书
            }
        }).build();
    } catch (Exception e) {
        logger.error("创建SSLContext异常", e);
        throw new RuntimeException(e);
    }

    // 2. 创建一个跳过主机名验证的 HostnameVerifier
    HostnameVerifier hostnameVerifier = (hostname, session) -> true;

    // 3. 创建 SSLConnectionSocketFactory,并传入 hostnameVerifier
    SSLConnectionSocketFactory sslFactory = new SSLConnectionSocketFactory(
        sslContext,
        hostnameVerifier  // ⚠️ 关键:必须传入这个参数!
    );

    return HttpClients.custom()
        .setSSLSocketFactory(sslFactory)
        .build();
}
private static SSLConnectionSocketFactory createSSLConnSocketFactory() throws Exception {
    // 1. 加载你的自签名证书(从 .jks 或 .p12 文件)
    FileInputStream fis = new FileInputStream("conf/tomcat.keystore");
    KeyStore trustStore = KeyStore.getInstance("PKCS12"); // 或 JKS
    trustStore.load(fis, "changeit".toCharArray()); // 密码
    fis.close();

    // 2. 构建 SSLContext,只信任这个 KeyStore 中的证书
    SSLContext sslContext = SSLContextBuilder.create()
        .loadTrustMaterial(trustStore, null) // 只信任 keystore 中的证书
        .build();

    return new SSLConnectionSocketFactory(sslContext);
}
// 从 keystore 中提取出服务器证书
Certificate cert = keyStore.getCertificate("tomcat"); // 别名是 tomcat

// 创建一个 TrustStrategy,只信任这个证书
TrustStrategy trustStrategy = (chain, authType) -> {
    if (chain == null || chain.length == 0) return false;
    return chain[0].equals(cert); // 只信任这个证书
};
keytool -genkeypair `
        -alias tomcat `
        -keyalg RSA `
        -keysize 2048 `
        -storetype PKCS12 `
        -keystore tomcat.keystore `
        -validity 365 `
        -dname "CN=zhumengyu, OU=CETC, O=WA, L=Nanjing, ST=Jiangsu, C=CN" `
        -ext "SAN=IP:192.151.20.2,IP:192.151.20.3,DNS:localhost,DNS:myapp.local" `
        -storepass changeit `
        -keypass changeit
格式扩展名平台/工具特点是否包台私钥
PEM.pem.crt,.key/,.certOpenSSH, OpenSSL, Nginx, ApacheBase64 编码的文本格式,可读可可包含
DER.der , .cerWindows, Java二进制格式看情况
JKS.jksJava, Tomcat (传统)Java 专属密钥库可包含多个
PKCS#12 / PFX.p12,.pfxWindows, Java, Apache加密打包的私钥+证书包含
PKCS#7.p7b ,.p7cWindows, Java只含证书链,不包含私钥不包含
CSR.csr所有平台证书签名请求 (非证书)只有公钥

证书常用命令

# OpenSSL 生成自签名证书(PEM 格式)
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
            -days 365 -nodes -subj "/CN=localhost"

# 查看证书内容
openssl x509 -in cert.pem -text -noout
# 查看私钥内容
openssl rsa -in key.pem -check -noout

# 使用openssl导出站点证书
openssl s_client -connect your-ip:port -showcerts </dev/null 2>/dev/null | openssl x509 -outform PEM > besland_cert.pem

# 生成证书指纹
openssl x509 -in tomcat.crt -fingerprint -sha256 -noout
 
# 转换 DER 到 PEM
openssl x509 -inform der -in cert.der -out cert.pem

# 将 PEM 转为 PKCS12
openssl pkcs12 -export -in cert.pem -inkey key.pem -out keystore.p12 -name "tomcat"

# 导出CSR 提交给 CA 后,CA 返回正式证书
openssl req -new -newkey rsa:2048 -nodes -keyout domain.key -out domain.csr

#从 PEM 转为 JKS(用于 Tomcat)
# 1. 合并证书链(可选)
cat domain.crt intermediate.crt > fullchain.pem
# 2. 转为 PKCS12
openssl pkcs12 -export -in fullchain.pem -inkey domain.key \
               -out keystore.p12 -name "tomcat"
# 3. 转为 JKS(或直接在 Tomcat 中用 P12)
keytool -importkeystore -srckeystore keystore.p12 -srcstoretype PKCS12 \
        -destkeystore keystore.jks -deststoretype JKS
        
# keytool 生成PKCS证书
keytool -genkeypair `
        -alias tomcat `
        -keyalg RSA `
        -keysize 2048 `
        -storetype PKCS12 `
        -keystore tomcat.keystore `
        -validity 365 `
        -dname "CN=zhumengyu, OU=CETC, O=WA, L=Nanjing, ST=Jiangsu, C=CN" `
        -ext "SAN=IP:192.151.20.2,IP:192.151.20.3,DNS:localhost,DNS:myapp.local" `
        -storepass changeit `
        -keypass changeit
# keytool 查看证书
keytool -list -v -keystore tomcat.keystore -alias tomcat

# keytool 导出证书
keytool -exportcert -keystore tomcat.keystore -alias tomcat -storepass changeit -file temp.crt

# 导入证书(默认密码: changeit)
# 找到JRE的cacerts文件位置
# 通常位于 $JAVA_HOME/lib/security/cacerts
keytool -import -alias besland -keystore $JAVA_HOME/lib/security/cacerts -file besland_cert.pem

#使用 openssl 工具来转换 PKCS12 文件(.keystore)为 PEM 格式:
# 1. 提取证书(cert.pem)
# -nokeys:不导出私钥,只导出证书。
# -passin pass:changeit:指定密钥库密码(changeit 是你设置的密码)。
openssl pkcs12 -in tomcat.keystore -nokeys -out cert.pem -passin pass:changeit

# 2. 提取私钥(key.pem)
# -nocerts:不导出证书,只导出私钥。
# -nodes:不加密私钥(否则会要求输入新的密码)。
openssl pkcs12 -in tomcat.keystore -nocerts -out key.pem -passin pass:changeit -nodes
💡✅ 最佳实践建议
  • ✅ 一个 tomcat.keystore 中只放一个 PrivateKeyEntry(避免混淆)
  • ✅ 导出 CSR 后,导入 CA 证书时确保是完整链(server + intermediate)
  • ✅ 内网测试时,可以把自签名证书导入 cacerts,让 Java 客户端信任它:

🔍 一、先看「签名」到底做了什么? 假设你有一段数据 M(比如一个文件、一条消息),你想向全世界证明: 这条消息是我发的(身份认证) 消息在传输中没被篡改(完整性) 📝 签名的标准流程(以 RSA 为例): 步骤 1️⃣:计算哈希(Hash) text

编辑

H = SHA256(M) 把任意长度的消息压缩成固定长度的“数字指纹”(如 256 位) 哈希不可逆,且微小改动会导致哈希巨变 步骤 2️⃣:用私钥对哈希进行“签名运算” text

编辑

Signature = RSA_Sign(H, 私钥) 这一步看起来像“加密”,但密码学上叫 “签名生成” 实际实现可能是:对哈希值做 RSA 私钥运算(带填充,如 PSS 或 PKCS#1 v1.5) 步骤 3️⃣:把 原始消息 M + 签名 Signature 一起发送出去 📌 注意:原始消息 M 是明文!没有被加密! 🔐 二、别人怎么验证? 接收方拿到 (M, Signature) 后: 步骤 1️⃣:用同样的哈希算法计算 H' = SHA256(M) 步骤 2️⃣:用你的公钥对 Signature 做“验签运算” text

编辑

H'' = RSA_Verify(Signature, 公钥) 步骤 3️⃣:比较 H' 和 H'' 如果相等 → 消息确实来自你,且未被篡改 ✅ 如果不等 → 要么消息被改,要么签名是伪造的 ❌