Java网络编程(UDP, TCP, HTTP)

发布于:2025-08-29 ⋅ 阅读:(19) ⋅ 点赞:(0)

1. OSI 七层网络模型

层级 名称 核心功能 协议示例 数据单元
7 应用层 提供用户接口和网络服务 HTTP, FTP, SMTP, DNS 报文
6 表示层 数据格式转换、加密/解密、压缩/解压 SSL, JPEG, MPEG 数据流
5 会话层 建立、管理和终止会话连接 NetBIOS, RPC 会话数据
4 传输层 端到端可靠传输、流量控制、差错校验 TCP, UDP 数据段
3 网络层 路由选择、逻辑寻址、分组转发 IP, ICMP, OSPF 数据包
2 数据链路层 物理寻址、帧同步、差错控制 Ethernet, PPP
1 物理层 比特流传输、物理接口定义 RS-232, 100Base-T 比特

2. 传输层协议

特性 UDP TCP
连接方式 无连接 面向连接(三次握手)
可靠性 不保证送达 可靠传输(ACK+重传)
数据边界 保留数据包边界 字节流(无边界)
传输速度 更快(无连接开销) 较慢(需维护连接状态)
头部开销 8 字节 20-60 字节
适用场景 视频流、DNS、实时游戏、广播 文件传输、网页浏览

2.1 UDP

UDP(User Datagram Protocol)是一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。

  1. 无连接传输
    • 通信前无需建立连接,直接发送数据包
    • java.net.DatagramSocket
  2. 不可靠传输
    • 不保证数据包顺序、不检测丢包、无重传机制
    • 传输效率高于 TCP(头部仅 8 字节)
  3. 面向数据报
    • 每次发送/接收都是完整数据包(有明确边界)
    • 数据包最大长度: 64KB - 8 字节(头部)
  4. 支持广播/多播
    • 可向同一网络内所有主机发送广播(地址:255.255.255.255)
    • 支持多播(组播)地址范围:224.0.0.0 ~ 224.255.255.255

2.1.1 发送数据

import java.net.*;

public class UDPSender {
    public static void main(String[] args) throws Exception {
        // 1. 创建Socket(随机端口)
        DatagramSocket socket = new DatagramSocket();

        // 2. 准备数据包(目标地址为localhost,端口8888)
        String message = "Hello UDP!";
        byte[] data = message.getBytes();
        InetAddress address = InetAddress.getByName("localhost");
        DatagramPacket packet = new DatagramPacket(data, data.length, address, 8888);

        // 3. 发送
        socket.send(packet);
        System.out.println("已发送: " + message);

        // 4. 关闭
        socket.close();
    }
}

2.1.2 接收数据

import java.net.*;

public class UDPReceiver {
    public static void main(String[] args) throws Exception {
        // 1. 创建Socket并绑定端口8888
        DatagramSocket socket = new DatagramSocket(8888);

        // 2. 准备空数据包(缓冲区)
        byte[] buffer = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

        // 3. 阻塞接收
        System.out.println("等待接收数据...");
        socket.receive(packet); // 阻塞直到收到数据

        // 4. 处理数据
        String message = new String(packet.getData(), 0, packet.getLength());
        System.out.println("收到来自" + packet.getAddress() + ":" + packet.getPort() + "的消息: " + message);

        // 5. 关闭
        socket.close();
    }
}

2.1.3 广播

// 发送广播示例
socket.setBroadcast(true); // 开启广播
InetAddress broadcastAddress = InetAddress.getByName("255.255.255.255");
DatagramPacket broadcastPacket = new DatagramPacket(data, data.length, broadcastAddress, 8888);
socket.send(broadcastPacket);

2.1.4 多播(组播)

发送

// 加入多播组(224.0.0.0~224.255.255.255)
InetAddress group = InetAddress.getByName("224.0.0.1");
MulticastSocket multicastSocket = new MulticastSocket();

// 发送数据到组
DatagramPacket packet = new DatagramPacket(data, data.length, group, 8888);
multicastSocket.send(packet);

接收

MulticastSocket multicastSocket = new MulticastSocket(8888);
multicastSocket.joinGroup(InetAddress.getByName("224.0.0.1")); // 加入组

// 接收数据(同普通UDP接收)
multicastSocket.receive(packet);

2.2 TCP

TCP(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。

  1. 面向连接:在数据传输之前,必须建立连接(三次握手),传输结束后要断开连接(四次挥手)。
  2. 可靠传输:通过确认机制、超时重传、流量控制、拥塞控制等机制保证数据正确到达。
  3. 字节流传输:数据被视为无结构的字节流,没有边界,但接收方收到的数据顺序与发送方发送的顺序一致。

2.2.1 三次握手

  1. 客户端发送 SYN(同步序列编号)包(SYN=1, seq=x)到服务器,进入 SYN_SENT 状态。
  2. 服务器收到 SYN 包,发送 SYN+ACK 包(SYN=1, ACK=1, seq=y, ack=x+1),进入 SYN_RECV 状态。
  3. 客户端收到 SYN+ACK 包,发送 ACK 包(ACK=1, seq=x+1, ack=y+1),进入 ESTABLISHED 状态。服务器收到 ACK 后也进入 ESTABLISHED 状态。

建立连接。

2.2.2 四次挥手

  1. 主动关闭方(假设为客户端)发送 FIN 包(FIN=1, seq=u),进入 FIN_WAIT_1 状态。
  2. 被动关闭方(服务器)收到 FIN,发送 ACK 包(ACK=1, seq=v, ack=u+1),进入 CLOSE_WAIT 状态。客户端收到 ACK 后进入 FIN_WAIT_2 状态。
  3. 服务器准备好关闭连接时,发送 FIN 包(FIN=1, seq=w, ack=u+1),进入 LAST_ACK 状态。
  4. 客户端收到 FIN,发送 ACK 包(ACK=1, seq=u+1, ack=w+1),进入 TIME_WAIT 状态,等待 2MSL(最大报文段生存时间)后关闭。服务器收到 ACK 后立即关闭。

断开连接。

2.2.3 实现

在 Java 中,我们可以使用 java.net.ServerSocketjava.net.Socket 类来实现 TCP 通信。

  1. 服务器端使用 ServerSocket 监听指定端口,等待客户端连接。当有客户端连接时,创建一个 Socket 对象,通过该对象进行通信。
  2. 客户端使用 Socket 连接到服务器,然后通过输出流向服务器发送数据,通过输入流读取服务器返回的数据。

服务器端

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {
    public static void main(String[] args) throws IOException {
        // 创建服务器Socket,监听8888端口
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务器启动,等待客户端连接...");

        // 等待客户端连接
        Socket socket = serverSocket.accept();
        System.out.println("客户端连接成功!");

        // 获取输入流,读取客户端数据
        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String message = in.readLine();
        System.out.println("收到客户端消息: " + message);

        // 获取输出流,向客户端发送数据
        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
        out.println("你好,客户端!");

        // 关闭资源
        in.close();
        out.close();
        socket.close();
        serverSocket.close();
    }
}

客户端

import java.io.*;
import java.net.Socket;

public class TCPClient {
    public static void main(String[] args) throws IOException {
        // 创建客户端Socket,连接服务器
        Socket socket = new Socket("localhost", 8888);
        System.out.println("已连接到服务器...");

        // 获取输出流,向服务器发送数据
        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
        out.println("你好,服务器!");

        // 获取输入流,读取服务器返回的数据
        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String response = in.readLine();
        System.out.println("收到服务器响应: " + response);

        // 关闭资源
        out.close();
        in.close();
        socket.close();
    }
}

3. 应用层协议

特性 HTTP HTTPS(HTTP+SSL/TLS)
安全性 明文传输(易被窃听) 加密传输(防窃听/篡改)
默认端口 80 443
性能 较高(无加密开销) 较低(加密/解密消耗 CPU)
证书 不需要 需要 CA 颁发的数字证书
适用场景 非敏感信息传输(如新闻网站) 敏感信息传输(如支付/登录)

3.1 HTTP

HTTP(HyperText Transfer Protocol)是应用层协议,基于 TCP/IP 协议族,用于在 Web 浏览器和服务器之间传输超文本(如 HTML)。
在 Java 中,我们可以使用 HttpURLConnection 或第三方库如 Apache HttpClient 来演示 HTTP 通信。

  1. 应用层协议

    1. 基于 TCP/IP 协议栈,用于 Web 浏览器和服务器之间的通信
    2. 默认端口:HTTP(80)/HTTPS(443)
    3. 遵循请求-响应模型:客户端发起请求,服务器返回响应
  2. 无状态协议

    1. 每个请求相互独立,服务器不保留客户端状态
    2. 通过 Cookie/Session 机制实现状态管理
  3. 报文结构

    1. 请求报文

      GET /index.html HTTP/1.1
      Host: www.example.com
      User-Agent: Java-HTTP-Client
      
    2. 响应报文

      HTTP/1.1 200 OK
      Content-Type: text/html
      Content-Length: 1024
      
      <!DOCTYPE html>...
      

3.1.1 URL 结构

标准格式:协议://主机[:端口]/路径?查询字符串#片段标识符
示例:http://example.com:8080/api/data?category=books#section2

3.1.2 HTTP 方法

方法 作用
GET 获取资源
POST 提交数据
PUT 更新资源
DELETE 删除资源
HEAD 获取响应头(无响应体)

3.1.3 状态码

状态码 类别 说明
1xx 信息响应 请求已被接收
2xx 成功 请求处理成功
3xx 重定向 需进一步操作
4xx 客户端错误 请求包含错误语法
5xx 服务器错误 服务器处理请求失败

3.1.4 发展

  1. HTTP/1.0:每个请求需单独建立连接
  2. HTTP/1.1:默认持久连接(可复用 TCP 连接)
  3. HTTP/2:二进制分帧、头部压缩、多路复用
  4. HTTP/3:基于 QUIC 协议(UDP 实现),解决队头阻塞

3.1.5 GET 请求

import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class HttpClientExample {
    public static void main(String[] args) throws Exception {
        URL url = new URL("http://example.com/api/data");
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        // 设置请求方法
        connection.setRequestMethod("GET");

        // 添加请求头
        connection.setRequestProperty("User-Agent", "Java HTTP Client");

        // 获取响应码
        int status = connection.getResponseCode();
        System.out.println("响应状态码: " + status);

        // 读取响应体
        try (BufferedReader reader = new BufferedReader(
            new InputStreamReader(connection.getInputStream()))) {
            String line;
            StringBuilder response = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            System.out.println("响应内容: " + response.toString());
        }

        // 断开连接
        connection.disconnect();
    }
}

3.1.6 POST 请求

import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class HttpPostExample {
    public static void main(String[] args) throws Exception {
        URL url = new URL("http://example.com/api/users");
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        // 设置请求方法
        connection.setRequestMethod("POST");
        connection.setDoOutput(true); // 允许输出

        // 设置请求头(JSON类型)
        connection.setRequestProperty("Content-Type", "application/json");

        // 准备JSON数据
        String jsonInput = "{\"name\": \"Alice\", \"age\": 30}";

        // 发送请求体
        try (OutputStream os = connection.getOutputStream()) {
            byte[] input = jsonInput.getBytes("utf-8");
            os.write(input, 0, input.length);
        }

        // 处理响应,同GET请求
        int status = connection.getResponseCode();
    }
}

3.1.7 超时

connection.setConnectTimeout(5000); // 5秒连接超时
connection.setReadTimeout(10000);    // 10秒读取超时

3.1.8 重定向

// 自动跟随重定向(默认true)
connection.setInstanceFollowRedirects(true);

// 手动处理重定向
if (status == HttpURLConnection.HTTP_MOVED_PERM) {
    String newUrl = connection.getHeaderField("Location");
    // 重新发起请求...
}

3.2 HTTPS

// 创建HTTPS连接
URL url = new URL("https://example.com");
HttpsURLConnection httpsConn = (HttpsURLConnection) url.openConnection();

// 配置SSL证书验证(生产环境需使用真实CA证书)
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{new X509TrustManager() {
    public void checkClientTrusted(X509Certificate[] chain, String authType) {}
    public void checkServerTrusted(X509Certificate[] chain, String authType) {}
    public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
}}, new SecureRandom());

httpsConn.setSSLSocketFactory(sslContext.getSocketFactory());
httpsConn.setHostnameVerifier((hostname, session) -> true); // 跳过主机名验证

// 后续操作与HTTP相同

3.3 WebSocket

WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,它允许服务端主动向客户端推送数据,非常适合实时应用。

特性 HTTP WebSocket
通信模式 半双工(请求-响应) 全双工(双向通信)
连接建立 每次请求都需要建立连接 一次握手,持久连接
头部开销 每次请求携带完整 HTTP 头(~800B) 初始握手后,数据帧头仅 2~14 字节
实时性 依赖轮询(高延迟) 实时推送(低延迟)
适用场景 传统网页浏览 实时聊天、股票行情、游戏
  1. 全双工实时通信
    1. 基于 TCP 的持久化连接协议(与 HTTP 互补)
    2. 服务端和客户端可同时双向传输数据(突破 HTTP 请求-响应限制)
    3. 默认端口:WS(80)/WSS(443),与 HTTP/HTTPS 端口一致
  2. 低开销高效传输
    1. 连接建立后,数据帧头部仅 2-14 字节(远小于 HTTP 头部)
    2. 避免 HTTP 轮询的资源浪费,适合实时应用(聊天、游戏等)
  3. 协议升级机制
    1. 通过 HTTP 升级握手建立连接:
      GET /chat HTTP/1.1
      Host: example.com
      Upgrade: websocket
      Connection: Upgrade
      Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
      Sec-WebSocket-Version: 13
      

3.3.1 服务端实现

使用 JSR 356 标准 API。

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/chat") // 声明 WebSocket 端点路径
public class ChatEndpoint {

    @OnOpen
    public void onOpen(Session session) {
        System.out.println("客户端连接: " + session.getId());
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("收到消息: " + message);
        // 广播消息给所有客户端
        session.getOpenSessions().forEach(s -> {
            try {
                s.getBasicRemote().sendText("Echo: " + message);
            } catch (Exception e) { e.printStackTrace(); }
        });
    }

    @OnClose
    public void onClose(Session session) {
        System.out.println("连接关闭: " + session.getId());
    }

    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }
}

3.3.2 客户端实现

import javax.websocket.*;
import java.net.URI;

@ClientEndpoint
public class WebSocketClient {

    @OnOpen
    public void onOpen(Session session) {
        System.out.println("连接服务器成功");
        try {
            session.getBasicRemote().sendText("Hello Server!");
        } catch (Exception e) { e.printStackTrace(); }
    }

    @OnMessage
    public void onMessage(String message) {
        System.out.println("收到服务端消息: " + message);
    }

    public static void main(String[] args) throws Exception {
        WebSocketContainer container = ContainerProvider.getWebSocketContainer();
        container.connectToServer(WebSocketClient.class,
            new URI("ws://localhost:8080/chat")); // WebSocket 地址
    }
}

3.3.3 二进制数据传输

@OnMessage
public void onMessage(ByteBuffer data, Session session) {
    byte[] bytes = new byte[data.remaining()];
    data.get(bytes);
    System.out.println("收到二进制数据长度: " + bytes.length);
}

// 发送二进制数据
session.getBasicRemote().sendBinary(ByteBuffer.wrap(new byte[]{0x48,0x65,0x6C,0x6C,0x6F}));

3.3.4 连接管理

// 获取所有活动会话
Set<Session> sessions = session.getOpenSessions();

// 异步发送(避免阻塞)
session.getAsyncRemote().sendText("异步消息");

3.3.5 安全连接 (WSS)

// 客户端连接使用 wss 协议
new URI("wss://example.com/chat");

// 服务端配置 SSL(Tomcat 示例)
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
           SSLEnabled="true" scheme="https" secure="true">
     <SSLHostConfig certificateVerification="none"/>
</Connector>

4. HTTP 报文

4.1 请求报文

纯文本格式传输。分为三个部分:请求行(Request Line)、请求头(Request Headers)、空行(CRLF)、请求体(Request Body)。

POST /login HTTP/1.1                                 -- 请求行
Host: www.example.com                                -- 请求头
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
User-Agent: Mozilla/5.0
                                                     -- 空行
username=admin&password=123                          -- 请求体

4.1.1 请求行(Request Line)

请求行是报文的第一行,包含三个关键元素,以空格分隔。

  1. 请求方法(Request Method):指定操作类型,如 GET(获取资源)、POST(提交数据)、PUT(更新资源)或 DELETE(删除资源)。
  2. 请求 URI(Request URI):标识目标资源路径,例如/index.html 或完整 URL 路径。
  3. 协议版本(Protocol Version):指定 HTTP 版本,如 HTTP/1.1 或 HTTP/2。

例如 GET /api/data HTTP/1.1:

  • GET: 请求方法
  • /api/data: 请求 URI
  • HTTP/1.1: 协议版本

4.1.2 请求头(Request Headers)

请求头从第二行开始,到第一个空行为止。

  • Host: www.example.com:指定服务器域名(HTTP/1.1必需)。
  • User-Agent: Mozilla/5.0:标识客户端类型。
  • Content-Type: application/json:定义请求体的媒体类型。
  • Authorization: Bearer token:用于认证。

4.1.3 请求体(Request Body)

请求体在空行之后。

主要用于 POST、PUT 等方法,提交表单、JSON 或文件。

  • 什么内容由 Content-Type 决定
    • application/x-www-form-urlencoded:表单数据(如 username=admin&password=123)。
    • application/json:JSON 数据(如{“name”: “John”})。
    • multipart/form-data:文件上传。

4.2 响应报文

响应报文结构通常包括:状态行(Status Line)、响应头(Response Headers)、空行(CRLF)、响应体(Response Body)。

HTTP/1.1 200 OK                                      -- 状态行
Content-Type: text/html; charset=UTF-8               -- 响应头
Content-Length: 1223
Last-Modified: Wed, 21 Oct 2015 14:26:38 GMT
                                                     -- 空行
<html>                                               -- 响应体
<body>
<h1>Hello, world!</h1>
</body>
</html>

4.2.1 状态行(Status Line)

位于报文首行。

  1. 协议版本:HTTP/1.1
    1. 定义 HTTP 协议版本(HTTP/1.0、HTTP/1.1、HTTP/2 等)
    2. 状态码: 200
  2. 三位数字代码,表示请求处理结果:
    1. 1xx:信息类(如 101 Switching Protocols)
    2. 2xx:成功(如 200 OK,201 Created)
    3. 3xx:重定向(如 301 Moved Permanently)
    4. 4xx:客户端错误(如 404 Not Found)
    5. 5xx:服务端错误(如 500 Internal Server Error)
  3. 原因短语:OK
    1. 状态码的文本描述(可自定义但通常遵循标准)

4.2.2 响应头(Response Headers)

键值对集合,每个头占一行,描述服务器信息和响应属性。

  • Content-Type : 响应体数据类型(必需)
    • text/html; charset=UTF-8
  • Content-Length : 响应体字节数(精确匹配实际长度
    • 1223
  • Last-Modified : 资源最后修改时间(用于缓存验证)
    • Wed, 21 Oct 2015 14:26:38 GMT
  • Cache-Control : 缓存控制指令
    • max-age=3600, public
  • Set-Cookie : 服务器设置客户端 Cookie
    • sessionId=abc123; Path=/; Secure
  • Location : 重定向目标 URL(配合 3xx 状态码)
    • https://newdomain.com/resource
  • Server : 服务器软件信息
    • Nginx/1.18.0
  • ETag : 资源版本标识符(用于缓存验证)
    • “33a64df551425fcc55e4d42a148795d9”

4.2.3 响应体(Response Body)

包含实际返回的资源数据,格式由 Content-Type 决定:

  • 文本类型:HTML、CSS、JSON 等可直接阅读
  • 二进制类型:如图片(image/jpeg)、压缩文件(application/zip)等

4.2.4 性能优化

  1. 启用压缩:Content-Encoding: gzip
  2. 缓存控制:Cache-Control: public, max-age=31536000

4.2.5 安全规范

  1. 敏感 Cookie 需加 Secure; HttpOnly 属性
  2. 跨域资源需设置 Access-Control-Allow-Origin

5. HTTP 请求头

5.1 通用请求头(General Headers)

适用于所有请求类型的头部。

  • Cache-Control
    控制缓存行为:no-cache(禁用缓存)、max-age=3600(缓存有效期)
  • Connection
    控制连接状态:keep-alive(保持连接)、close(关闭连接)
  • Upgrade
    请求协议升级:Upgrade: websocket(升级到 WebSocket 协议)
  • Via
    显示请求经过的代理路径:Via: 1.1 proxy1, 1.1 proxy2

5.2 实体头(Entity Headers)

描述请求体内容的头部。

  • Content-Length
    请求体字节数:Content-Length: 348(精确长度必须匹配)
  • Content-Type 请求体数据类型:
    application/json(JSON 数据)
    multipart/form-data(文件上传)
    application/x-www-form-urlencoded(表单数据)
  • Content-Encoding
    请求体压缩格式:gzip、deflate、br
  • Content-Language
    请求体语言:zh-CN、en-US

5.3 请求控制头(Request Control Headers)

控制请求处理逻辑的头部。

  • Host(必需)
    目标服务器域名:Host: www.example.com(HTTP/1.1强制要求)
  • User-Agent
    客户端标识:
    Mozilla/5.0 (Windows NT 10.0; Win64; x64)
    AppleWebKit/537.36 (KHTML, like Gecko)
    Chrome/91.0.4472.124 Safari/537.36
    
  • Referer
    请求来源 URL:Referer: https://www.google.com/(用于流量分析)

5.4 内容协商头(Content Negotiation)

客户端声明可接受的内容类型。

  • Accept
    响应内容类型偏好:Accept: text/html, application/xhtml+xml;q=0.9(q 值表示权重)
  • Accept-Encoding
    可接受的压缩格式:gzip, deflate, br
  • Accept-Language
    语言偏好:Accept-Language: zh-CN, en-US;q=0.7
  • Accept-Charset
    字符集偏好:Accept-Charset: utf-8, iso-8859-1(现代浏览器通常忽略)

5.5 认证头(Authentication Headers)

身份验证相关头部。

  • Authorization
    身份凭证:
    • Basic dXNlcjpwYXNz(Base64 编码)
    • Bearer eyJhbGci…(JWT 令牌)
  • Proxy-Authorization
    代理服务器认证:Proxy-Authorization: Basic YWxhZGRpbjp…

5.6 条件请求头(Conditional Headers)

基于资源状态的条件请求。

  • If-Match
    ETag 匹配检查:If-Match: “737060cd8c284d8af7ad3082f209582d”
  • If-None-Match
    ETag 不匹配时请求:用于缓存验证(返回 304 Not Modified)4
  • If-Modified-Since
    时间戳检查:If-Modified-Since: Wed, 21 Oct 2025 07:28:00 GMT
  • If-Unmodified-Since
    资源未修改时操作:用于并发控制

5.7 特殊用途头(Special Purpose Headers)

特定场景使用的头部。

  • Range
    请求部分内容:Range: bytes=0-499(断点续传)
  • Origin
    跨域请求来源:Origin: https://www.domain.com(CORS必需)
  • Cookie
    客户端存储数据:Cookie: sessionId=38afes7a8; userId=john
  • DNT (Do Not Track)
    隐私请求:DNT: 1(请求不跟踪用户行为)

6. HTTP 响应头

6.1 基础控制头

  • Content-Type
    指定响应体的媒体类型(MIME 类型)和字符编码,例如:Content-Type: text/html; charset=utf-8 表示返回 HTML 文档,使用 UTF-8 编码。
  • Content-Length
    声明响应体的字节长度,例如:Content-Length: 1024 表示响应体大小为 1KB。
  • Transfer-Encoding
    指定传输编码方式,常见值:
    • chunked:响应体分块传输(动态内容常用)
    • gzip:响应体压缩传输。

6.2 缓存控制头

  • Cache-Control
    控制缓存行为,常用指令:
    • no-cache:强制向服务器验证缓存
    • max-age=3600:资源有效期 1 小时
    • public:允许中间代理缓存。
  • Expires
    设定资源过期时间(GMT 格式),例如:Expires: Wed, 21 Oct 2025 07:28:00 GMT
  • ETag
    资源版本标识符(如文件哈希值),用于缓存验证:ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"

6.3 重定向与刷新头

  • Location
    配合 3xx 状态码实现重定向,指定新 URL:Location: https://example.com/new-page

  • Refresh
    设置页面自动刷新/跳转:Refresh: 5; url=https://example.com 表示 5 秒后跳转到指定 URL。

6.4 安全控制头

  • Content-Security-Policy (CSP)
    定义内容安全策略,防止 XSS 攻击:Content-Security-Policy: default-src 'self' 表示仅允许加载同源资源。

  • Strict-Transport-Security (HSTS)
    强制 HTTPS 连接:Strict-Transport-Security: max-age=31536000 表示 1 年内自动转 HTTPS。

6.5 特殊功能头

  • Content-Disposition
    控制文件下载行为:Content-Disposition: attachment; filename="report.pdf" 触发文件下载并指定文件名。
  • Set-Cookie
    服务器向客户端设置 Cookie:Set-Cookie: sessionid=38afes7a8; HttpOnly; Secure HttpOnly 禁止 JS 访问,Secure 仅限 HTTPS 传输。
  • Access-Control-Allow-Origin
    跨域资源共享(CORS)关键头:Access-Control-Allow-Origin: * 允许所有域访问资源。

6.6 服务器信息头

  • Server
    暴露服务器类型和版本:Server: nginx/1.18.0(建议隐藏以减少攻击面)。
  • X-Powered-By
    显示后端技术栈(如 PHP 版本):X-Powered-By: PHP/7.4.3

7. Cookie, Session

  1. Cookie:客户端存储机制(≤4KB),通过 Set-Cookie 头创建
  2. Session:服务器端存储机制,通过 Session ID 标识客户端
  3. 区别:
    • Cookie 数据存储在浏览器,Session 数据存储在服务器
    • Cookie 可长期保存,Session 随会话结束失效
    • Cookie 有 4KB 限制,Session 无硬性限制

8. JWT

JWT(JSON Web Token)是目前最流行的跨域认证解决方案,特别适用于分布式站点的单点登录(SSO)场景。
JWT的最大优势是服务器不再需要存储Session状态,使得服务器认证鉴权业务可以方便扩展。

特性 Session 机制 JWT(JSON Web Token)
存储位置 服务器端(内存/数据库) 客户端(localStorage/Cookie)
数据结构 会话 ID(无状态标识) 自包含 JSON(Header.Payload.Signature)
通信方式 通过 Cookie 传递 Session ID 通过 HTTP Header 或 URL 参数传递
状态管理 有状态(服务器存储会话数据) 无状态(所有信息在 Token 中)
扩展性 集群部署需 Session 共享方案 天然支持分布式系统
  1. 如果Token存储在localStorage中,可能面临XSS攻击的风险(因为JavaScript可以读取)。如果存储在HttpOnly的Cookie中,则相对安全,但也要注意CSRF。
  2. JWT的Token体积通常比Session ID大,每次请求都会在HTTP头部中携带,增加带宽消耗。
  3. 一旦签发,在有效期内无法撤销。
  4. Token中可以直接存储用户信息(如用户ID、角色等),服务器解析Token即可获取,无需额外查询。

8.1 前端实现

8.1.1 登录获取 JWT

登录获取JWT并存进Cookie中。

async function login() {
  const res = await fetch('http://localhost:8080/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ 
      username: 'admin', 
      password: 'password123' 
    }),
    credentials: 'include' // 必须包含凭据
  });
  
  if (res.ok) alert('登录成功!Cookie已设置');
}

8.1.2 访问受保护资源

自动携带Cookie中的JWT发送请求。

async function fetchData() {
  const res = await fetch('/protected', {
    credentials: 'include' // 自动发送Cookie
  });
  
  if (res.ok) {
    const data = await res.text();
    alert('获取数据: ' + data);
  } else {
    alert('访问失败: ' + res.status);
  }
}

8.2 后端实现

需要添加jsonwebtoken包,以SpringBoot进行演示。

8.2.1 登录接口

后端签发设置HttpOnly、Secure和SameSite属性以增强安全性(如防止XSS和CSRF攻击)。

@RestController
public class AuthController {
    
    @PostMapping("/login")
    public ResponseEntity<String> login(
        @RequestBody LoginRequest request, 
        HttpServletResponse response) {
        
        // 1. 验证用户名密码
        if ("admin".equals(request.username()) && "password123".equals(request.password())) {
            
            // 2. 生成JWT
            String jwt = JwtUtil.generateToken(request.username());
            
            // 3. 创建安全Cookie
            Cookie cookie = new Cookie("jwt", jwt);
            cookie.setHttpOnly(true);  // 防止XSS攻击
            cookie.setSecure(true);    // 仅HTTPS传输
            cookie.setPath("/");       // 全局路径
            cookie.setMaxAge(3600);    // 1小时有效期
            cookie.setAttribute("SameSite", "Strict"); // 防止CSRF
            
            // 4. 添加到响应头
            response.addCookie(cookie);
            return ResponseEntity.ok("登录成功");
        }
        return ResponseEntity.status(401).body("认证失败");
    }
}

8.2.2 受保护资源接口

@GetMapping("/protected")
public ResponseEntity<String> protectedResource(
    @CookieValue("jwt") String token) {  // 自动从Cookie提取
    
    if (JwtUtil.validateToken(token)) {
        return ResponseEntity.ok("访问成功!这是受保护资源");
    }
    return ResponseEntity.status(401).body("无效令牌");
}

8.2.3 JwtUtil

public class JwtUtil {
    // 生成带IP绑定的JWT
    public static String generateToken(String username, String ip) {
        return Jwts.builder()
                .setSubject(username)
                .claim("ip", ip) // 绑定客户端IP
                .setExpiration(new Date(System.currentTimeMillis() + 3600000))
                .signWith(Keys.hmacShaKeyFor(SECRET.getBytes()))
                .compact();
    }
    
    // 验证时检查IP
    public static boolean validateToken(String token, String clientIp) {
        Claims claims = Jwts.parserBuilder()
            .setSigningKey(SECRET.getBytes())
            .build()
            .parseClaimsJws(token)
            .getBody();
            
        return claims.get("ip").equals(clientIp);
    }
}

8.2.4 CORS配置

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("https://yourdomain.com")
                .allowedMethods("*")
                .allowCredentials(true); // 必须允许凭据
    }
}

网站公告

今日签到

点亮在社区的每一天
去签到