Update
This commit is contained in:
@@ -0,0 +1,553 @@
|
||||
---
|
||||
tags: [CS, NET, security, authorization, web, authentication-flow]
|
||||
create time: 2026-04-17
|
||||
---
|
||||
|
||||
# Web Application Authorization Flow
|
||||
|
||||
## 概述
|
||||
|
||||
**Authorization(授权)** 在 Web 应用中是 HTTP 层面的认证令牌验证机制。本文聚焦浏览器、服务器、Token 的交互流程和加密验证过程。
|
||||
|
||||
## HTTP Authorization 流程
|
||||
|
||||
### 完整授权流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant B as Browser
|
||||
participant S as Server
|
||||
participant DB as Database
|
||||
|
||||
Note over B,S: 1. 登录获取 Token
|
||||
B->>S: POST /login (username, password)
|
||||
S->>DB: 验证凭证
|
||||
DB-->>S: 用户数据
|
||||
S->>S: HMAC-SHA256(payload, secret)
|
||||
S-->>B: {access_token: "eyJhbGc...", expires_in: 3600}
|
||||
|
||||
Note over B,S: 2. 带授权头访问资源
|
||||
B->>B: 存储 Token (LocalStorage/Memory)
|
||||
B->>S: GET /api/resource
|
||||
Note right of B: Authorization: Bearer eyJhbGc...
|
||||
|
||||
Note over S: 3. 验证 Token
|
||||
S->>S: 提取 Token
|
||||
S->>S: 验证签名 (HMAC)
|
||||
S->>S: 检查过期时间
|
||||
S->>S: 验证 Issuer/Audience
|
||||
S->>DB: 根据 user_id 查询权限
|
||||
|
||||
Note over S: 4. 授权决策
|
||||
alt Token 有效
|
||||
S-->>B: 200 OK + 数据
|
||||
else Token 无效
|
||||
S-->>B: 401 Unauthorized
|
||||
else 权限不足
|
||||
S-->>B: 403 Forbidden
|
||||
end
|
||||
```
|
||||
|
||||
### JWT Token 验证过程
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[接收 Authorization: Bearer <token>] --> B[解析三部分]
|
||||
B --> C[Header: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9]
|
||||
B --> D[Payload: eyJ1c2VyX2lkIjoiMTIzIiw...]
|
||||
B --> E[Signature: SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c]
|
||||
|
||||
C --> F[获取签名算法 HS256]
|
||||
D --> G[获取用户数据]
|
||||
E --> H[签名部分]
|
||||
|
||||
F --> I[重新计算签名]
|
||||
G --> I
|
||||
H --> I
|
||||
|
||||
I --> J{签名匹配?}
|
||||
J -->|是| K[检查过期时间 exp]
|
||||
J -->|否| L[拒绝: 401 Unauthorized]
|
||||
|
||||
K -->|未过期| M[检查 nbf 时间]
|
||||
K -->|已过期| L
|
||||
|
||||
M -->|有效| N[检查 iss 签发者]
|
||||
M -->|无效| L
|
||||
|
||||
N -->|匹配| O[验证通过]
|
||||
N -->|不匹配| L
|
||||
|
||||
O --> P[从 payload 提取 user_id]
|
||||
P --> Q[查询用户权限]
|
||||
Q --> R[返回受保护资源]
|
||||
```
|
||||
|
||||
## 浏览器行为详解
|
||||
|
||||
### 浏览器如何处理 Authorization
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[用户请求资源] --> B[检查 Token]
|
||||
B --> C{Token 存在于?}
|
||||
|
||||
C -->|LocalStorage| D[从 LS 读取]
|
||||
C -->|SessionStorage| E[从 SS 读取]
|
||||
C -->|Memory| F[从变量读取]
|
||||
C -->|Cookie| G[浏览器自动发送]
|
||||
|
||||
D --> H[构造 Authorization Header]
|
||||
E --> H
|
||||
F --> H
|
||||
G --> I[无需手动添加<sup>*</sup>]
|
||||
|
||||
H --> J[发起 XHR/Fetch 请求]
|
||||
I --> J
|
||||
|
||||
J --> K[设置 headers]
|
||||
K --> L[发送到服务器]
|
||||
|
||||
style G fill:#ff9999
|
||||
style H fill:#90EE90
|
||||
style I fill:#90EE90
|
||||
|
||||
subgraph 注
|
||||
G1[Cookie 方式需要 SameSite 属性]
|
||||
G2[防止 CSRF 攻击]
|
||||
end
|
||||
|
||||
G1 -.-> G
|
||||
G2 -.-> G
|
||||
```
|
||||
|
||||
**浏览器自动发送 Cookie,但需要手动添加 Bearer Token**
|
||||
|
||||
### 浏览器存储 Token 的方式
|
||||
|
||||
| 存储方式 | 特点 | 安全性 | 跨域访问 |
|
||||
|---------|------|--------|---------|
|
||||
| LocalStorage | 持久化,手动管理 | ❌ XSS 风险 | ✅ 支持 |
|
||||
| SessionStorage | 会话结束清除 | ❌ XSS 风险 | ❌ 仅同源 |
|
||||
| Memory | 页面刷新丢失 | ✅ 最安全 | ❌ 仅当前页面 |
|
||||
| Cookie | 可设置 HttpOnly | ✅ 防 XSS | ⚠️ 需 SameSite |
|
||||
|
||||
### 前端实现 (JavaScript)
|
||||
|
||||
```javascript
|
||||
// 1. 登录后存储 Token
|
||||
async function login(username, password) {
|
||||
const response = await fetch('/api/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username, password })
|
||||
});
|
||||
|
||||
const { access_token } = await response.json();
|
||||
localStorage.setItem('token', access_token);
|
||||
}
|
||||
|
||||
// 2. 带认证头访问 API
|
||||
async function fetchProtectedResource() {
|
||||
const token = localStorage.getItem('token');
|
||||
|
||||
const response = await fetch('/api/resource', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.status === 401) {
|
||||
// Token 过期,需要刷新或重新登录
|
||||
handleTokenExpired();
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
// 3. Axios 拦截器自动添加 Token
|
||||
axios.interceptors.request.use(config => {
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
```
|
||||
|
||||
## 加密与签名机制
|
||||
|
||||
### JWT 签名算法对比
|
||||
|
||||
| 算法类型 | 算法 | 密钥类型 | 特点 |
|
||||
| ----- | ----- | ----- | ------------------- |
|
||||
| HMAC | HS256 | 对称密钥 | 服务器签发和验证都 uses 同一密钥 |
|
||||
| HMAC | HS512 | 对称密钥 | 更强的哈希,性能稍低 |
|
||||
| RSA | RS256 | 非对称密钥 | 私钥签名,公钥验证 |
|
||||
| RSA | RS512 | 非对称密钥 | 更强的签名算法 |
|
||||
| ECDSA | ES256 | 椭圆曲线 | 比更短但同样安全 |
|
||||
|
||||
### HMAC-SHA256 签名流程
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[原始数据] --> B[Base64URL 编码 Header]
|
||||
A --> C[Base64URL 编码 Payload]
|
||||
|
||||
B --> D[拼接 Header.Payload]
|
||||
C --> D
|
||||
|
||||
D --> E[HMAC-SHA256 拼接结果]
|
||||
E --> F[使用 Secret Key]
|
||||
F --> G[生成 256-bit 签名]
|
||||
|
||||
G --> H[Base64URL 编码签名]
|
||||
H --> I[拼接 final JWT]
|
||||
|
||||
I --> J[Header.Payload.Signature]
|
||||
|
||||
style E fill:#ff9999
|
||||
style F fill:#ff9999
|
||||
style G fill:#lightblue
|
||||
```
|
||||
|
||||
**签名 = HMAC-SHA256(Base64URL(Header) + "." + Base64URL(Payload), Secret)**
|
||||
|
||||
### Go 实现签名与验证
|
||||
|
||||
```go
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type JWTBuilder struct {
|
||||
secretKey []byte
|
||||
}
|
||||
|
||||
// 生成签名
|
||||
func (j *JWTBuilder) sign(header, payload string) string {
|
||||
data := strings.Join([]string{header, payload}, ".")
|
||||
h := hmac.New(sha256.New, j.secretKey)
|
||||
h.Write([]byte(data))
|
||||
signature := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
|
||||
return signature
|
||||
}
|
||||
|
||||
// 验证签名
|
||||
func (j *JWTBuilder) verify(token string) bool {
|
||||
parts := strings.Split(token, ".")
|
||||
if len(parts) != 3 {
|
||||
return false
|
||||
}
|
||||
|
||||
header, payload, signature := parts[0], parts[1], parts[2]
|
||||
|
||||
// 重新计算签名
|
||||
expectedSignature := j.sign(header, payload)
|
||||
|
||||
// 比较签名 (恒定时间比较,防止计时攻击)
|
||||
return hmac.Equal([]byte(signature), []byte(expectedSignature))
|
||||
}
|
||||
```
|
||||
|
||||
## 服务器验证流程
|
||||
|
||||
### 详细的验证步骤
|
||||
|
||||
```go
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Claims struct {
|
||||
UserID string `json:"user_id"`
|
||||
exp int64 `json:"exp"`
|
||||
iat int64 `json:"iat"`
|
||||
iss string `json:"iss"`
|
||||
aud string `json:"aud"`
|
||||
}
|
||||
|
||||
func (m *AuthMiddleware) validateToken(token string) (*Claims, error) {
|
||||
// 步骤 1: 分割三部分
|
||||
parts := strings.Split(token, ".")
|
||||
if len(parts) != 3 {
|
||||
return nil, fmt.Errorf("invalid token format")
|
||||
}
|
||||
|
||||
// 步骤 2: 验证签名
|
||||
if !m.verifySignature(token) {
|
||||
return nil, fmt.Errorf("invalid signature")
|
||||
}
|
||||
|
||||
// 步骤 3: 解码 Payload
|
||||
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid payload encoding")
|
||||
}
|
||||
|
||||
// 步骤 4: 解析 Claims
|
||||
var claims Claims
|
||||
if err := json.Unmarshal(payload, &claims); err != nil {
|
||||
return nil, fmt.Errorf("invalid payload json")
|
||||
}
|
||||
|
||||
// 步骤 5: 验证过期时间
|
||||
if time.Now().Unix() > claims.exp {
|
||||
return nil, fmt.Errorf("token expired")
|
||||
}
|
||||
|
||||
// 步骤 6: 验证签发者
|
||||
if claims.iss != "my-app" {
|
||||
return nil, fmt.Errorf("invalid issuer")
|
||||
}
|
||||
|
||||
// 步骤 7: 验证受众
|
||||
if claims.aud != "api-users" {
|
||||
return nil, fmt.Errorf("invalid audience")
|
||||
}
|
||||
|
||||
return &claims, nil
|
||||
}
|
||||
|
||||
// 验证签名
|
||||
func (m *AuthMiddleware) verifySignature(token string) bool {
|
||||
parts := strings.Split(token, ".")
|
||||
signatureData := parts[0] + "." + parts[1]
|
||||
providedSignature := parts[2]
|
||||
|
||||
// 计算期望的签名
|
||||
expectedSignature := m.sign(signatureData)
|
||||
|
||||
// 恒定时间比较
|
||||
return hmac.Equal(
|
||||
[]byte(providedSignature),
|
||||
[]byte(expectedSignature),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### HTTP 中间件
|
||||
|
||||
```go
|
||||
// AuthMiddleware 验证每个请求
|
||||
func (m *AuthMiddleware) Handler(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// 1. 提取 Authorization 头
|
||||
authHeader := r.Header.Get("Authorization")
|
||||
if authHeader == "" {
|
||||
http.Error(w, "Missing Authorization header", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// 2. 解析 Bearer Token
|
||||
if !strings.HasPrefix(authHeader, "Bearer ") {
|
||||
http.Error(w, "Invalid authorization scheme", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
token := strings.TrimPrefix(authHeader, "Bearer ")
|
||||
|
||||
// 3. 验证 Token
|
||||
claims, err := m.validateToken(token)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// 4. 将用户信息存入 Context
|
||||
ctx := context.WithValue(r.Context(), "user_id", claims.UserID)
|
||||
ctx = context.WithValue(ctx, "claims", claims)
|
||||
|
||||
// 5. 继续处理请求
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 常见 Authorization Scheme
|
||||
|
||||
### Basic Auth(基础认证)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant B as Browser
|
||||
participant S as Server
|
||||
participant DB as Database
|
||||
|
||||
B->>S: GET /resource
|
||||
S-->>B: 401 Unauthorized
|
||||
Note right of B: WWW-Authenticate: Basic realm="Secure Area"
|
||||
|
||||
B->>B: 用户输入用户名密码
|
||||
B->>B: Base64("username:password")
|
||||
B->>S: GET /resource
|
||||
Note right of B: Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
|
||||
|
||||
S->>B: 解码 Base64
|
||||
S->>DB: 验证用户名密码
|
||||
DB-->>S: 验证结果
|
||||
S-->>B: 200 OK + 资源
|
||||
```
|
||||
|
||||
**问题**:每次请求都需要用户名密码,不安全,不推荐
|
||||
|
||||
### Digest Auth(摘要认证)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as Client
|
||||
participant S as Server
|
||||
|
||||
C->>S: GET /resource
|
||||
S-->>C: 401 Unauthorized
|
||||
Note right of C: WWW-Authenticate: Digest<br/>realm="Protected",<br/>nonce="xyz123",<br/>qop="auth"
|
||||
|
||||
C->>C: 计算 response<br/>MD5(username:realm:password)<br/>MD5(method:uri)<br/>HA1 = MD5(username:realm:password)<br/>HA2 = MD5(method:uri)<br/>response = MD5(HA1:nonce:HA2)
|
||||
|
||||
C->>S: GET /resource
|
||||
Note right of C: Authorization: Digest<br/>username="user",<br/>realm="Protected",<br/>nonce="xyz123",<br/>uri="/resource",<br/>response="abc123"
|
||||
|
||||
S->>S: 重新计算 response
|
||||
S-->>C: 200 OK
|
||||
```
|
||||
|
||||
**优点**:密码不直接传输,更安全
|
||||
**缺点**:实现复杂,需要多次往返
|
||||
|
||||
### Bearer Token(持有者令牌)
|
||||
|
||||
✅ **现代 Web 应用的标准选择**
|
||||
|
||||
```http
|
||||
# 请求示例
|
||||
GET /api/user/profile HTTP/1.1
|
||||
Host: api.example.com
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMTIzIiwidXNlcm5hbWUiOiJqb2huIiwiZXhwIjoxNzI4MjQ4MDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
|
||||
|
||||
# 响应示例
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"user_id": "123",
|
||||
"username": "john",
|
||||
"email": "john@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
## Refresh Token 机制
|
||||
|
||||
### Access Token vs Refresh Token
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[Access Token] --> B[有效期短<br/>15-30分钟]
|
||||
A --> C[用于 API 访问]
|
||||
A --> D[存储在浏览器]
|
||||
|
||||
E[Refresh Token] --> F[有效期长<br/>数天至数周]
|
||||
E --> G[用于获取新 Access Token]
|
||||
E --> H[存储在 HttpOnly Cookie]
|
||||
|
||||
style A fill:#90EE90
|
||||
style E fill:#ffcc00
|
||||
```
|
||||
|
||||
### Token 刷新流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant B as Browser
|
||||
participant S as Server
|
||||
|
||||
Note over B,S: Access Token 过期
|
||||
B->>S: POST /api/refresh
|
||||
Note right of B: Body: {refresh_token: "..."}
|
||||
|
||||
S->>S: 验证 Refresh Token
|
||||
S->>S: 生成新的 Access Token
|
||||
S-->>B: {access_token: "new_jwt..."}
|
||||
|
||||
Note over B,S: 使用新 Token 继续请求
|
||||
B->>S: GET /api/resource
|
||||
Note right of B: Authorization: Bearer new_jwt...
|
||||
|
||||
S-->>B: 200 OK
|
||||
```
|
||||
|
||||
**Refresh Token 优势**:
|
||||
- ✅ Access Token 短期有效,减少泄露风险
|
||||
- ✅ Refresh Token 可随时撤销
|
||||
- ✅ 用户无感知自动续期
|
||||
|
||||
## 安全最佳实践
|
||||
|
||||
### Token 安全传输
|
||||
|
||||
| 措施 | 说明 | Go 实现 |
|
||||
|-----|------|---------|
|
||||
| **HTTPS 强制** | 防止 Token 被窃取 | RedirectHTTPS 中间件 |
|
||||
| **短期有效期** | 降低被滥用风险 | Token 15-30 分钟 |
|
||||
| **签名验证** | 防止 Token 被篡改 | HMAC/RSA 签名 |
|
||||
| **黑名单机制** | 主动撤销 Token | Redis 存储 revoked_tokens |
|
||||
|
||||
### 浏览器安全设置
|
||||
|
||||
```javascript
|
||||
// ✅ 推荐:HttpOnly Cookie 存储 Refresh Token
|
||||
document.cookie = `refresh_token=${refreshToken}; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=604800`;
|
||||
|
||||
// ⚠️ 谨慎:LocalStorage 存储 Access Token
|
||||
localStorage.setItem('access_token', accessToken);
|
||||
// 需要 XSS 防护
|
||||
|
||||
// ❌ 避免:明文传输 Token
|
||||
// 必须使用 HTTPS
|
||||
```
|
||||
|
||||
### CORS 和 SameSite
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[前端应用<br/>example.com] --> B[后端 API<br/>api.example.com]
|
||||
|
||||
B --> C[CORS 策略]
|
||||
C --> D[Access-Control-Allow-Origin: https://example.com]
|
||||
C --> E[Access-Control-Allow-Credentials: true]
|
||||
|
||||
B --> F[SameSite Cookie]
|
||||
F --> G[Strict: 严格模式]
|
||||
F --> H[Lax: 放宽模式]
|
||||
|
||||
style D fill:#90EE90
|
||||
style G fill:#90EE90
|
||||
```
|
||||
|
||||
**Go CORS 配置**:
|
||||
```go
|
||||
func setupCORS() *cors.Cors {
|
||||
return cors.New(cors.Options{
|
||||
AllowedOrigins: []string{"https://example.com"},
|
||||
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"},
|
||||
AllowedHeaders: []string{"Authorization", "Content-Type"},
|
||||
AllowCredentials: true,
|
||||
MaxAge: 3600,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 相关笔记
|
||||
|
||||
- [[金山办公作业/Week05/用户认证]] - 认证的实现方式
|
||||
- [[CS/NET/HTTPS]] - SSL/TLS 加密传输
|
||||
- [[CS/DB/访问控制]] - 数据库层面权限
|
||||
@@ -0,0 +1,349 @@
|
||||
---
|
||||
tags: [CS, NET, algorithm, data-stream, heavy-hitter]
|
||||
create time: 2026-04-17 13:45
|
||||
---
|
||||
|
||||
# HeavyKeeper
|
||||
|
||||
## 概述
|
||||
|
||||
HeavyKeeper 是一种用于高速数据流中检测 Heavy Hitters(频繁项)的高效算法。它在保持常数内存空间的同时,能够准确地识别出出现频率超过设定阈值的数据项,广泛应用于网络流量监测、热点检测等场景。
|
||||
|
||||
## 算法原理
|
||||
|
||||
### 核心思想
|
||||
|
||||
HeavyKeeper 结合了 Count-Min Sketch 和守桶策略,通过多层哈希守桶机制来提高准确性。其核心目标是区分"大象流"和"老鼠流"。
|
||||
|
||||
#### 什么是大象流和老鼠流?
|
||||
|
||||
在数据流分析中,通常将数据流按频率分为两类:
|
||||
|
||||
| 类型 | 特征 | 频率占比 | 典型例子 |
|
||||
|------|------|----------|---------|
|
||||
| **大象流**(Elephant Flow) | 高频出现 | 占总流量的大部分 | 热门IP、热门搜索词、DDoS攻击流量 |
|
||||
| **老鼠流**(Mouse Flow) | 低频偶发 | 数量众多但频率极低 | 少量用户访问、正常连接请求 |
|
||||
|
||||
**关键洞察**:在很多场景中,**80-90%的流量来自不到1%的源**,这就是大象流。HeavyKeeper 的目标就是高效识别这些大象流,过滤掉老鼠流。
|
||||
|
||||
### 守桶机制
|
||||
|
||||
#### 什么是"守桶"?
|
||||
|
||||
"守桶"(Keeper)是 HeavyKeeper 的核心创新。每个桶会"守护"一个特定的数据项:
|
||||
|
||||
- 当数据流中的一个项到来时,哈希到某个桶
|
||||
- 如果这个项正好是该桶"守护"的项,就直接计数
|
||||
- 如果不是,则根据概率决定是否"抢夺"守护权
|
||||
|
||||
**底层原理**:让大象流(高频项)能够长期占据守桶位置,而老鼠流(低频项)很难长期占用桶的资源。
|
||||
|
||||
#### 桶的结构
|
||||
|
||||
每个桶维护以下信息:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| **item** | 数据项 | 当前守护的数据项 |
|
||||
| **count** | 整数 | 守护项的精确计数 |
|
||||
| **error** | 整数 | 误差估计(记录非守护项经过的次数) |
|
||||
|
||||
#### 守桶策略:大象流如何压制老鼠流
|
||||
|
||||
**替换概率公式**:
|
||||
```python
|
||||
替换概率 = min(1, 新项估计频率 / 当前守护项计数)
|
||||
```
|
||||
|
||||
这个公式的直观含义:
|
||||
|
||||
| 情况 | 新项类型 | 替换概率 | 结果 |
|
||||
|------|---------|---------|------|
|
||||
| 大象流 vs 老鼠流 | 老鼠流(freq≈1) | 1/count | 极小,**老鼠流无法撼动大象流** |
|
||||
| 老鼠流 vs 老鼠流 | 老鼠流(freq≈2) | 2/count | 较小,随机性强 |
|
||||
| 大象流 vs 老鼠流 | 大象流(freq=50) | 50/5=1 | 必然替换,**新大象流抢占桶** |
|
||||
| 大象流 vs 大象流 | 大象流(freq=98) | 98/95≈1 | 可能替换,两个大象流竞争 |
|
||||
|
||||
**例子**:
|
||||
```
|
||||
桶#100 当前守护:IP=10.0.0.1 (count=100, error=5) ← 大象流
|
||||
新到来:IP=10.0.0.2 → 哈希到桶#100 ← 老鼠流
|
||||
|
||||
替换概率 = min(1, 1/100) = 0.01
|
||||
|
||||
结果:0.95(随机数)> 0.01 → 不替换
|
||||
解释:大象流继续守护,老鼠流只能默默增加error
|
||||
```
|
||||
|
||||
#### 多层哈希的作用
|
||||
|
||||
单层哈希可能发生冲突(多个项哈希到同一个桶),多层哈希通过冗余来解决:
|
||||
|
||||
- 同一个项会由L个不同的哈希函数映射到L层的不同桶
|
||||
- 查询时取所有层的最小值(保守估计)
|
||||
- 即使部分桶冲突,也能获得准确的下界
|
||||
|
||||
**最终计数** = min(所有层中该项的count值)
|
||||
|
||||
#### Heavy Hitters 检测流程
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[数据流新项 x] --> B[计算L个哈希]
|
||||
B --> C[访问L个桶]
|
||||
|
||||
C --> D{是否为守护项?}
|
||||
D -->|是| E[count++]
|
||||
D -->|否| F[计算替换概率]
|
||||
|
||||
F --> G{触发替换?}
|
||||
G -->|是| H[替换并重置count=1]
|
||||
G -->|否| I[error++]
|
||||
|
||||
E --> J[继续]
|
||||
H --> J
|
||||
I --> J
|
||||
```
|
||||
|
||||
查找Top K时,只需遍历所有桶,收集 `count ≥ 阈值` 的候选项。
|
||||
|
||||
### 衰减机制
|
||||
|
||||
#### 为什么需要衰减?
|
||||
|
||||
**问题场景**:
|
||||
```
|
||||
10:00-10:05 IP=10.0.0.1 出现 1000 次 → 成为大象流,占据桶
|
||||
10:06-12:00 IP=10.0.0.1 不再出现,但其count=1000依然存在
|
||||
12:01 IP=10.0.0.2 频繁出现,但无法抢占count=1000的桶
|
||||
```
|
||||
|
||||
如果不衰减,过时的大象流会持续占用资源,阻碍新大象流的检测。
|
||||
|
||||
#### 衰减机制的工作原理
|
||||
|
||||
HeavyKeeper 通过周期性衰减来解决这个问题:
|
||||
|
||||
**方法1:时间窗口衰减(推荐)**
|
||||
```python
|
||||
def periodic_decay():
|
||||
每经过 Δt 时间,所有桶的 count 和 error 乘以衰减因子 α
|
||||
count = count × α
|
||||
error = error × α
|
||||
其中 α ∈ (0, 1),通常 α = 0.9 或 0.99
|
||||
```
|
||||
|
||||
**方法2:基于老化(Aging)**
|
||||
```python
|
||||
def aging(bucket, current_time):
|
||||
elapsed = current_time - bucket.last_update_time
|
||||
decay = exp(-λ × elapsed) # λ 是衰减速率
|
||||
bucket.count = bucket.count × decay
|
||||
```
|
||||
|
||||
#### 衰减机制的数学效果
|
||||
|
||||
**时间窗口视角**:
|
||||
```
|
||||
衰减因子 α = 0.99,窗口大小 = N
|
||||
|
||||
N时刻前的权重:0.99^N ≈ 0.366 ← 仅保留36.6%
|
||||
2N时刻前的权重:0.99^2N ≈ 0.134 ← 只保留13.4%
|
||||
```
|
||||
|
||||
这意味着:越久远的计数对当前统计影响越小,让算法能够"遗忘"过时的流。
|
||||
|
||||
#### 衰减规则示例
|
||||
|
||||
| 场景 | 原 count | 衰减后 count | 解析 |
|
||||
|------|----------|-------------|------|
|
||||
| 持续活跃的大象流 | 1000 | 990 (×0.99) | 持续补充,衰减不影响地位 |
|
||||
| 最近消失的大象流 | 1000 | 366 (×0.99^100) | 100个周期后快速衰减,让出桶 |
|
||||
| 新大象流 | 0 → 10 | 10 (刚开始) | 有机会竞争已衰减的桶 |
|
||||
|
||||
#### 衰减带来的好处
|
||||
|
||||
| 优势 | 说明 |
|
||||
|------|------|
|
||||
| **自适应流行度漂移** | 热点变化时,旧热点会自动失去守桶权 |
|
||||
| **滑动窗口效果** | 只关注最近时间窗口内的频率,而非历史总和 |
|
||||
| **防止资源垄断** | 过时的大象流不会长期占用桶资源 |
|
||||
|
||||
#### 衰减与守桶的协同
|
||||
|
||||
衰减机制和守桶机制协同工作,形成一个动态平衡:
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[大象流活跃] --> B[count快速累积]
|
||||
B --> C[占据守桶位置]
|
||||
|
||||
C --> D{时间流逝}
|
||||
D -->|持续活跃| E[保持守桶]
|
||||
D -->|停止活跃| F[衰减降低count]
|
||||
|
||||
F --> G{新竞争者?}
|
||||
G -->|有| H[被替换,让出桶]
|
||||
G -->|无| I[继续衰减直至清理]
|
||||
```
|
||||
|
||||
## 关键参数
|
||||
|
||||
| 参数 | 说明 | 典型值 |
|
||||
|------|------|--------|
|
||||
| **m** | 每层桶的数量 | 2^15 ~ 2^20 |
|
||||
| **L** | 哈希层数 | 3 ~ 5 |
|
||||
| **θ** | 频率阈值 | 0.001 ~ 0.01 |
|
||||
| **α** | 衰减因子 | 0.9 ~ 0.99 |
|
||||
| **Δt** | 衰减周期 | 根据应用场景 |
|
||||
|
||||
## 性能特征
|
||||
|
||||
### 空间复杂度
|
||||
- 空间复杂度: **O(m × L)**
|
||||
- 每桶存储: item (~8字节) + count (~4字节) + error (~4字节)
|
||||
|
||||
### 时间复杂度
|
||||
|
||||
| 操作 | 时间复杂度 | 说明 |
|
||||
|------|-----------|------|
|
||||
| **插入** | O(L) | 对每层进行哈希和更新 |
|
||||
| **查询** | O(L) | 取所有层最小值 |
|
||||
| **衰减** | O(m×L) | 批量处理所有桶 |
|
||||
|
||||
## 优势与局限
|
||||
|
||||
### 优势
|
||||
- **大象流识别准确**: 守桶机制确保高频项持续占据资源
|
||||
- **老鼠流过滤**: 低频项很难干扰大象流统计
|
||||
- **自适应流行度变化**: 衰减机制处理热点漂移
|
||||
- **内存高效**: 恒定空间,不受数据流规模影响
|
||||
|
||||
### 局限性
|
||||
- **参数敏感**: m、L、α 等参数需要根据数据特征调优
|
||||
- **哈希冲突**: 极端情况下可能产生误报或漏报
|
||||
- **衰减延迟**: 热点切换时需要一定时间生效
|
||||
|
||||
## 应用场景
|
||||
|
||||
### 典型用例
|
||||
|
||||
1. **网络流量分析**: 识别高频 IP 地址或端口(大象流)
|
||||
2. **DDoS 防护**: 检测异常高频流量,过滤老鼠流
|
||||
3. **实时推荐**: 发现用户偏好热点,利用衰减实现热点漂移
|
||||
4. **CDN 缓存**: 识别热门内容进行预加载
|
||||
5. **日志分析**: 快速定位高频错误或异常事件
|
||||
|
||||
### 实际部署考虑
|
||||
|
||||
| 场景 | 大象流示例 | 老鼠流示例 | 衰减建议 |
|
||||
|------|-----------|-----------|---------|
|
||||
| 网络带宽监控 | P2P下载、视频流 | 正常网页浏览 | 较慢衰减(α=0.99) |
|
||||
| DDoS检测 | 攻击源IP | 正常用户IP | 快速衰减(α=0.9) |
|
||||
| 搜索热门 | 热门关键词 | 长尾搜索 | 中等衰减(α=0.95) |
|
||||
|
||||
## 参考实现
|
||||
|
||||
### 伪代码
|
||||
|
||||
```python
|
||||
class HeavyKeeper:
|
||||
def __init__(self, m, L, threshold, decay_factor):
|
||||
self.m = m # 每层桶数
|
||||
self.L = L # 哈希层数
|
||||
self.threshold = threshold
|
||||
self.decay_factor = decay_factor # 衰减因子
|
||||
|
||||
# 初始化多层 sketch
|
||||
self.buckets = [[KeeperBucket() for _ in range(m)]
|
||||
for _ in range(L)]
|
||||
|
||||
# 初始化哈希函数
|
||||
self.hash_funcs = [get_hash_func(i) for i in range(L)]
|
||||
|
||||
def insert(self, item, timestamp):
|
||||
for layer in range(self.L):
|
||||
idx = self.hash_funcs[layer](item) % self.m
|
||||
bucket = self.buckets[layer][idx]
|
||||
|
||||
if bucket.item == item:
|
||||
# 守护项匹配,直接计数(大象流强化)
|
||||
bucket.count += 1
|
||||
bucket.last_seen = timestamp
|
||||
else:
|
||||
# 计算替换概率
|
||||
estimated_freq = self._estimate_freq(item)
|
||||
replace_prob = min(1, estimated_freq / bucket.count)
|
||||
|
||||
if random.random() < replace_prob:
|
||||
# 替换为新项(大象流夺权)
|
||||
bucket.item = item
|
||||
bucket.count = 1
|
||||
bucket.error = bucket.count
|
||||
bucket.last_seen = timestamp
|
||||
else:
|
||||
# 不替换,仅增加误差(老鼠流被阻拦)
|
||||
bucket.error += 1
|
||||
|
||||
def apply_decay(self, current_time):
|
||||
"""应用衰减机制"""
|
||||
for layer in range(self.L):
|
||||
for bucket in self.buckets[layer]:
|
||||
elapsed = current_time - bucket.last_seen
|
||||
if elapsed > DECAY_INTERVAL:
|
||||
bucket.count *= self.decay_factor
|
||||
bucket.error *= self.decay_factor
|
||||
|
||||
# 归零清理
|
||||
if bucket.count < 1:
|
||||
bucket.item = None
|
||||
bucket.count = 0
|
||||
bucket.error = 0
|
||||
|
||||
def query(self, item):
|
||||
"""查询Item的频率估计"""
|
||||
min_count = float('inf')
|
||||
for layer in range(self.L):
|
||||
idx = self.hash_funcs[layer](item) % self.m
|
||||
bucket = self.buckets[layer][idx]
|
||||
if bucket.item == item:
|
||||
min_count = min(min_count, bucket.count)
|
||||
|
||||
return min_count if min_count != float('inf') else 0
|
||||
|
||||
def get_top_k(self, k):
|
||||
"""获取Top K大象流"""
|
||||
candidates = {}
|
||||
for layer in range(self.L):
|
||||
for bucket in self.buckets[layer]:
|
||||
if bucket.count >= self.threshold and bucket.item:
|
||||
item = bucket.item
|
||||
candidates[item] = max(candidates.get(item, 0),
|
||||
bucket.count)
|
||||
|
||||
# 返回 Top-k
|
||||
return sorted(candidates.items(),
|
||||
key=lambda x: x[1],
|
||||
reverse=True)[:k]
|
||||
```
|
||||
|
||||
## 相关算法对比
|
||||
|
||||
| 算法 | 空间复杂度 | 大象流准确性 | 老鼠流过滤 | 衰减支持 | 适用场景 |
|
||||
|------|-----------|------------|-----------|---------|---------|
|
||||
| **HeavyKeeper** | O(m×L) | 高 | 优秀 | 原生支持 | 高速数据流,需检测热点漂移 |
|
||||
| **Count-Min** | O(m×L) | 中 | 无 | 需额外实现 | 通用频率统计 |
|
||||
| **SpaceSaving** | O(k) | 中 | 好 | 手动实现 | 固定数量Top-K |
|
||||
| **LossyCounter** | O(kε) | 高 | 一般 | 手动实现 | 离线精确统计 |
|
||||
|
||||
## 参考资料
|
||||
|
||||
- HeavyKeeper: Streaming Heavy Hitters Detection with Known Error Bounds (2020)
|
||||
- Count-Min Sketch: An Improved Data Stream Summary
|
||||
- Streaming Algorithms for Finding Heavy Hitters
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- [[CS/NET/Count-Min-Sketch.md]]
|
||||
- [[CS/DS/HashMap.md]]
|
||||
- [[CS/NET/Network-Flow-Analysis.md]]
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
tags: [CS, NET, network]
|
||||
create time: 2026-04-17
|
||||
---
|
||||
|
||||
# 计算机网络
|
||||
|
||||
## 概述
|
||||
|
||||
计算机网络是物理上分散的计算机通过通信线路连接起来,在网络软件的管理下实现资源共享和信息传递的系统。
|
||||
|
||||
## 核心概念
|
||||
|
||||
### OSI七层模型
|
||||
- 物理层、数据链路层、网络层
|
||||
- 传输层、会话层、表示层、应用层
|
||||
|
||||
### TCP/IP协议栈
|
||||
- IP协议、TCP/UDP协议
|
||||
- HTTP/HTTPS、DNS、SMTP等应用层协议
|
||||
- 网络地址与路由
|
||||
|
||||
### 网络架构
|
||||
- 客户端-服务器架构
|
||||
- P2P架构
|
||||
- 分布式系统
|
||||
|
||||
### 网络安全
|
||||
- 加密与解密
|
||||
- SSL/TLS
|
||||
- 防火墙与VPN
|
||||
|
||||
## 学习重点
|
||||
|
||||
- TCP/IP协议详解
|
||||
- HTTP协议与Web服务
|
||||
- 套接字编程
|
||||
- 网络性能优化
|
||||
|
||||
## 实践建议
|
||||
|
||||
- 使用Wireshark抓包分析
|
||||
- 编写网络应用
|
||||
- 搭建本地网络环境
|
||||
- 理解常见网络问题
|
||||
@@ -0,0 +1,208 @@
|
||||
---
|
||||
tags: [network, protocol, theory, cs]
|
||||
create time: 2026-04-17 12:15
|
||||
---
|
||||
|
||||
# 网络协议分析基础
|
||||
|
||||
## 概述
|
||||
|
||||
网络协议分析是理解网络通信的核心技术,通过分析数据包的结构和内容,可以深入理解网络协议的工作原理和通信机制。
|
||||
|
||||
## 正文
|
||||
|
||||
### TCP 三次握手与四次挥手
|
||||
|
||||
#### 三次握手 (SYN, SYN-ACK, ACK)
|
||||
|
||||
```
|
||||
客户端 服务器
|
||||
| |
|
||||
| --- SYN, seq=x --------------------> |
|
||||
| |
|
||||
| <--- SYN, ACK, seq=y, ack=x+1 ------ |
|
||||
| |
|
||||
| --- ACK, seq=x+1, ack=y+1 ---------> |
|
||||
| | 连接建立
|
||||
```
|
||||
|
||||
**各字段含义:**
|
||||
- **SYN**: 同步标志,用于建立连接
|
||||
- **ACK**: 确认标志,表示确认收到
|
||||
- **seq**: 序列号,确保数据有序传递
|
||||
- **ack**: 确认号,表示期望收到的下一个序列号
|
||||
|
||||
**抓包特征:**
|
||||
1. 第一个包:SYN 标志位为 1,ACK 为 0
|
||||
2. 第二个包:SYN 和 ACK 都为 1
|
||||
3. 第三个包:ACK 为 1,SYN 为 0
|
||||
|
||||
#### 四次挥手 (FIN, ACK, FIN, ACK)
|
||||
|
||||
```
|
||||
客户端 服务器
|
||||
| |
|
||||
| --- FIN, seq=x --------------------> | 主动关闭
|
||||
| |
|
||||
| <--- ACK, seq=y, ack=x+1 ----------- |
|
||||
| | 半关闭
|
||||
| <--- FIN, seq=y ------------------- | 被动关闭
|
||||
| |
|
||||
| --- ACK, seq=x+1, ack=y+1 ---------> |
|
||||
| | 连接关闭
|
||||
```
|
||||
|
||||
**状态转换:**
|
||||
- FIN_WAIT_1: 主动关闭方发送 FIN
|
||||
- FIN_WAIT_2: 主动关闭方收到 ACK,等待对方 FIN
|
||||
- CLOSE_WAIT: 被动关闭方收到 FIN,进入半关闭状态
|
||||
- LAST_ACK: 被动关闭方发送 FIN
|
||||
- TIME_WAIT: 主动关闭方收到 FIN,等待 2MSL 后完全关闭
|
||||
|
||||
### HTTP 协议分析
|
||||
|
||||
#### 请求结构
|
||||
|
||||
```
|
||||
Method Request-URI HTTP-Version\r\n
|
||||
Header-Name: Header-Value\r\n
|
||||
\r\n
|
||||
Message-Body
|
||||
```
|
||||
|
||||
**常见方法:**
|
||||
- **GET**: 获取资源
|
||||
- **POST**: 提交数据
|
||||
- **PUT**: 更新资源
|
||||
- **DELETE**: 删除资源
|
||||
- **HEAD**: 获取响应头
|
||||
- **OPTIONS**: 获取支持的方法
|
||||
|
||||
**常用请求头:**
|
||||
```
|
||||
Host: example.com
|
||||
User-Agent: Mozilla/5.0
|
||||
Accept: text/html,application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer token
|
||||
Cookie: session=xxx
|
||||
```
|
||||
|
||||
#### 响应结构
|
||||
|
||||
```
|
||||
HTTP-Version Status-Code Reason-Phrase\r\n
|
||||
Header-Name: Header-Value\r\n
|
||||
\r\n
|
||||
Message-Body
|
||||
```
|
||||
|
||||
**状态码分类:**
|
||||
- **2xx**: 成功 (200 OK, 201 Created, 204 No Content)
|
||||
- **3xx**: 重定向 (301 Moved Permanently, 302 Found, 304 Not Modified)
|
||||
- **4xx**: 客户端错误 (400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found)
|
||||
- **5xx**: 服务器错误 (500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable)
|
||||
|
||||
**常用响应头:**
|
||||
```
|
||||
Content-Type: application/json; charset=utf-8
|
||||
Content-Length: 1234
|
||||
Cache-Control: max-age=3600
|
||||
ETag: "abc123"
|
||||
Set-Cookie: session=xxx; Path=/; HttpOnly
|
||||
```
|
||||
|
||||
### TLS/SSL 加密通信
|
||||
|
||||
#### TLS 握手流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as 客户端
|
||||
participant S as 服务器
|
||||
|
||||
C->>S: ClientHello<br/>(支持的加密套件、随机数)
|
||||
S-->>C: ServerHello<br/>(选择的加密套件、随机数、证书)
|
||||
S-->>C: Certificate<br/>(服务器证书)
|
||||
S-->>C: ServerHelloDone
|
||||
|
||||
C->>S: ClientKeyExchange<br/>(预主密钥,用服务器公钥加密)
|
||||
C->>S: ChangeCipherSpec<br/>(通知后续使用加密通信)
|
||||
C->>S: Finished<br/>(握手完成,加密验证)
|
||||
|
||||
S-->>C: ChangeCipherSpec
|
||||
S-->>C: Finished
|
||||
|
||||
Note over C,S: 开始加密通信
|
||||
```
|
||||
|
||||
**关键概念:**
|
||||
- **证书链**: 从服务器证书到根证书的信任链
|
||||
- **预主密钥**: 通过非对称加密传输,用于生成会话密钥
|
||||
- **会话密钥**: 通过预主密钥和双方随机数生成,用于对称加密
|
||||
- **SSL Pinning**: 客户端验证服务器证书,防止中间人攻击
|
||||
|
||||
### 数据包分析要点
|
||||
|
||||
#### 抓包指标
|
||||
|
||||
**性能指标:**
|
||||
- **RTT (Round Trip Time)**: 往返时延
|
||||
- **吞吐量**: 单位时间传输的数据量
|
||||
- **丢包率**: 丢失的数据包比例
|
||||
- **重传率**: 重发数据包的比例
|
||||
|
||||
**连接指标:**
|
||||
- **TCP 窗口大小**: 接收窗口和拥塞窗口
|
||||
- **连接状态**: ESTABLISHED, TIME_WAIT 等
|
||||
- **连接复用**: Keep-Alive, HTTP/2 连接复用
|
||||
|
||||
#### 过滤技巧
|
||||
|
||||
**基于协议过滤:**
|
||||
```
|
||||
# HTTP/HTTPS
|
||||
http or http2 or ssl
|
||||
|
||||
# TCP 特定标志
|
||||
tcp.flags.syn == 1 # SYN 包
|
||||
tcp.flags.ack == 1 # ACK 包
|
||||
tcp.flags.fin == 1 # FIN 包
|
||||
tcp.flags.reset == 1 # RST 包
|
||||
```
|
||||
|
||||
**基于内容过滤:**
|
||||
```
|
||||
# 特定 User-Agent
|
||||
http.user_agent contains "Chrome"
|
||||
|
||||
# 特定域名
|
||||
http.host == "example.com"
|
||||
|
||||
# HTTP 错误
|
||||
http.response.code >= 400
|
||||
|
||||
# 请求体内容
|
||||
http.file_data contains "keyword"
|
||||
```
|
||||
|
||||
**基于网络层过滤:**
|
||||
```
|
||||
# 源/目标地址
|
||||
ip.src == 192.168.1.1
|
||||
ip.dst == 192.168.1.1
|
||||
|
||||
# 端口范围
|
||||
tcp.port >= 1024 and tcp.port <= 65535
|
||||
```
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- [[CS/TOOLS/网络抓包]]
|
||||
- [[CS/SECURITY/网络安全基础]]
|
||||
- [[CS/OS/TCP 协议详解]]
|
||||
|
||||
## 参考资源
|
||||
|
||||
- RFC 文档: https://www.rfc-editor.org/
|
||||
- Wireshark 指南: https://www.wireshark.org/docs/wsug_html_chunked/
|
||||
Reference in New Issue
Block a user