返回首页

net/http 中的动态路由:DeadDrop 实现

本文分析了 DeadDrop 服务在纯 net/http 中动态路由和 Cookie 认证的实现。手动路径解析、HMAC 令牌、用例架构。带测试的完整周期。

net/http:DeadDrop 中的动态路由和 Cookie 认证
Advertisement 728x90

net/http中的动态路由与Cookie认证:实现DeadDrop服务

继续我们关于纯net/http的系列,我们为DeadDrop服务实现动态路由。用户将能够通过类似/secret/{id}的链接查看秘密。标准mux在Go 1.22之前不支持路径参数,因此我们编写自己的解析器。

处理程序在/secret/处注册,带有尾部斜杠——这会捕获所有子路径。我们手动从r.URL.Path中提取ID。

func extractIDFromPath(path string) (string, bool) {
    const prefix = "/secret/"

    if !strings.HasPrefix(path, prefix) {
        return "", false
    }

    id := strings.TrimPrefix(path, prefix)
    if id == "" || strings.Contains(id, "/") {
        return "", false
    }

    return id, true
}

该函数检查前缀,修剪它,并排除无效ID(空或包含/)。这提供了针对格式错误请求的基本保护。

Google AdInline article slot

使用依赖注入的SecretHandler

我们创建一个处理程序结构体用于依赖注入:

type SecretHandler struct {
    // 依赖注入占位符
}

func NewSecretHandler() *SecretHandler {
    return &SecretHandler{}
}

func (h *SecretHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    id, ok := extractIDFromPath(r.URL.Path)
    if !ok {
        http.NotFound(w, r)
        return
    }
    w.Header().Set("Content-Type", "text/plain; charset=utf-8")
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("请求的秘密ID: " + id))
}

router.go中注册:mux.Handle("/secret/", secretHandler)。测试边界情况:

  • /secret/12345/ → 404
  • /secret/ → 404
  • /secret/axQ/233 → 404
  • /secret/ad31H234 → 成功返回ID

检索秘密的用例

定义请求和响应:

Google AdInline article slot
type GetSecretRequest struct {
    ID       string
    Password string
}

type GetSecretResponse struct {
    ID        string
    Message   string
    FileName  string
    FileExt   string
    HasFile   bool
    ExpiresAt time.Time
}

用例逻辑:

  • 从存储中检索秘密
  • 检查过期时间(使用time.After()),删除过期的秘密
  • 通过CheckPasswordHash验证密码
  • 返回数据或错误

Cookie认证

为了保护文件下载,我们通过cookie实现认证。密码验证成功后,设置一个包含加密访问令牌的会话cookie。

  • 优点:无状态、可扩展、无需会话存储
  • Cookie参数:HttpOnly、Secure、SameSite=Strict

当请求/download/{id}时:

Google AdInline article slot
  • 从路径中提取ID
  • 读取auth_token cookie
  • 解码并验证令牌(ID + 密码哈希 + 随机数)
  • 如果有效——提供文件

访问令牌结构

令牌格式为base64(ID + ":" + hash + ":" + nonce),其中:

  • hash = HMAC-SHA256(ID + password, secretKey)
  • nonce——一次性数字,防止重放攻击

验证示例:

func validateDownloadToken(id, token string) bool {
    // 解码令牌
    // 提取哈希和随机数
    // 重新计算HMAC
    // 比较
    return true // 或false
}

完整秘密工作流程

  • 创建:POST /create → 生成ID、哈希密码、保存
  • 查看:GET /secret/{id} → 密码输入表单
  • 认证:POST密码 → cookie + 重定向
  • 下载:GET /download/{id} → 检查cookie → 文件
  • 清理:TTL过期 → 删除

UI原型:带有密码字段和“打开”按钮的页面。成功后——显示内容 + 文件链接。

路由测试

| 测试路径 | 预期结果 | 实际结果 |

|-----------|-------------|--------|

| /secret/123/ | 404 | 404 |

| /secret/ | 404 | 404 |

| /secret/id/with/slash | 404 | 404 |

| /secret/valid123 | 成功:"ID: valid123" | 成功 |

边界情况覆盖了解析器的安全性。

关键要点

  • 动态路由:手动解析r.URL.Path,无需第三方路由器
  • 安全性:ID验证、斜杠检查、秘密的TTL
  • 认证:使用带HMAC令牌的cookie替代会话
  • 架构:用例 + 依赖注入,处理程序外的清晰逻辑
  • 可扩展性:无状态,适合集群部署

— Editorial Team

Advertisement 728x90

继续阅读