api key必须放在header(authorization或x-api-key),禁用query参数;校验须用中间件/interceptor统一处理;比对需constanttimecompare,存储用bcrypt哈希而非加密;支持多key、过期与禁用状态。

API Key该放在Header还是Query参数?
必须放 Authorization 或自定义 Header(如 X-API-Key),绝不能走 Query。Query 会出现在访问日志、代理缓存、CDN 日志甚至浏览器地址栏里,等于把钥匙贴在玻璃门上。
- HTTP/2 下所有 Header 自动转小写,服务端要用
md.Get("x-api-key"),不是md.Get("X-API-Key")(gRPC 场景)或r.Header.Get("X-API-Key")(HTTP 场景) - 如果和 JWT 共存,建议统一用
Authorization: APIKey xxx格式,避免和Bearer混淆;但更推荐独立键名,比如X-API-Key,语义清晰且不冲突 - Query 传 key 的唯一“合理”场景是公开只读接口(如天气开放数据),且需配合 IP 限频 + 短期有效期,但生产级后端应杜绝
校验逻辑写在哪?中间件 or Handler内部?
必须抽成独立中间件(HTTP)或 UnaryInterceptor(gRPC),否则每次加新接口都要复制粘贴,密钥轮换、审计日志、失败计数全得改十几次。
- HTTP 示例中,
authMiddleware应在路由注册时统一挂载:http.HandleFunc("/api/data", authMiddleware(dataHandler)) - gRPC 场景下,务必用
grpc.UnaryInterceptor,别在每个func(ctx, req)里手写校验 —— 一旦漏一个,就等于留了后门 - 校验失败时,HTTP 返回
http.StatusUnauthorized,gRPC 必须用status.Error(codes.Unauthenticated, "invalid api key"),不能只return nil, nil或忽略错误
如何安全存储和比对 API Key?
别用 == 直接比较字符串,存在时序攻击风险;也别把密钥明文写死在代码里,哪怕只是测试环境。
- 比对必须用
crypto/subtle.ConstantTimeCompare,例如:subtle.ConstantTimeCompare([]byte(storedKey), []byte(clientKey)) == 1 - 密钥应从环境变量加载:
os.Getenv("API_KEY_SECRET"),开发时可用.env配合godotenv,但上线必须由运维注入 - 数据库存 API Key 时,**不要加密,要哈希**:用
bcrypt.GenerateFromPassword存哈希值,验证时用bcrypt.CompareHashAndPassword—— 加密意味着可逆,一旦密钥库泄露,所有 key 都直接暴露
为什么需要支持多 Key 和自动过期?
单 Key 是运维灾难的起点:某次发布导致 key 泄露,你得立刻停服更新,所有客户端同步改配置;没做 key 过期,旧系统长期带病运行,审计时根本说不清谁在调用。
- 数据库表至少包含:
id,access_key(明文,用于查询),secret_hash(bcrypt 哈希),expires_at,disabled - 校验前先查
expires_at > NOW()和disabled = false,两者任一不满足就拒绝,不进哈希比对流程 - 轮换时保留旧 key 7 天,新旧 key 同时有效,给客户端缓冲期;日志里记录
access_key而非完整 secret,避免日志泄露
最常被跳过的其实是「禁用状态」字段——开发阶段觉得没必要,上线后发现某个合作方滥用接口,却没法秒级关停,只能临时加黑名单规则,这就是设计时少想了一步的代价。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











