traefik网关鉴权中间件转发multipart/form-data请求的multipart: NextPart: EOF问题

发布于:2025-08-06 ⋅ 阅读:(14) ⋅ 点赞:(0)

1 问题

我有一个上传文件接口,实际请求是:

curl --location --request POST 'https://api.test.cn/file/upload' \
--header 'Authorization: 123' \
--header 'Connection: keep-alive' \
--header 'Content-Type: multipart/form-data; boundary=--------------------------045547357906535257406124' \
--form 'file=@"/tmp/1.txt"'

报错:

multipart: NextPart: EOF

2 原因分析

发现直接请求 pod 是可以的:

curl --location --request POST 'http://10.119.2.154:8080/file/upload' \
--header 'Authorization: 123' \
--header 'Connection: keep-alive' \
--header 'Content-Type: multipart/form-data; boundary=--------------------------045547357906535257406124' \
--form 'file=@"/tmp/1.txt"'

问题可能出现在网关层,我使用的是 Traefik 网关,查看 Traefik 日志有如下报错:

2025-08-04T02:58:38Z DBG github.com/traefik/traefik/v3/pkg/middlewares/auth/forward.go:180 > Remote error http://auth-service:10090/user/auth. StatusCode: 400 middlewareName=user-auth@kubernetescrd middlewareType=ForwardAuth 10.119.0.192 - - 
[04/Aug/2025:02:58:38 +0000] "POST /file/upload HTTP/1.1" 400 25 "-" "-" 3 "auth-api-route-ca8f0cd5d19177e62b04@kubernetescrd" "-" 2ms

user-auth 是一个 forwardAuth 中间件,用于做全局的用户鉴权,返回了400

Traefik forwardAuth 中间件配置为:

  forwardAuth:
    address: http://auth-service:10090/user/auth
    authResponseHeadersRegex: ^X-Test
    trustForwardHeader: true

Traefik  未显式定义 authRequestHeaders,那它会将原始请求的所有头信息和完整 Body 都转发给认证服务,实际转发内容为:

  • 所有 Headers(包括 Content-Type: multipart/form-data)
  • 完整的文件二进制数据(通过 Body 传输)

而鉴权接口(/user/auth)只是为了校验 token,其入参解析逻辑为(使用go-zero框架):

// 认证接口入参解析
if err := httpx.Parse(r, &req); err != nil {
   httpx.ErrorCtx(r.Context(), w, err)
   return
}

// go-zero源码 rest/httpx/requests.go
func Parse(r *http.Request, v any) error {
   kind := mapping.Deref(reflect.TypeOf(v)).Kind()
   if kind != reflect.Array && kind != reflect.Slice {
      if err := ParsePath(r, v); err != nil {
         return err
      }
      if err := ParseForm(r, v); err != nil {
         return err
      }
      if err := ParseHeaders(r, v); err != nil {
         return err
      }
   }
   if err := ParseJsonBody(r, v); err != nil {
      return err
   }
   if valid, ok := v.(validation.Validator); ok {
      return valid.Validate()
   } else if val := getValidator(); val != nil {
      return val.Validate(r, v)
   }
   return nil
}

也就是说,解析器解析到 Content-Type 是 multipart/form-data 格式,会尝试去解析这个包含文件二进制数据的 Body,但是鉴权接口并没有定义文件字段,解析器因格式不匹报了错:multipart: NextPart: EOF

3 解决

第一种:在 forwardAuth 中间件中显式定义转发的请求头

定义 authRequestHeaders,指定好需要转发哪些 Header,不要转发 Content-Type 

forwardAuth:
    address: http://auth-service:10090/user/auth
    authRequestHeaders:
      - Authorization
      - X-Test
    authResponseHeadersRegex: ^X-Test
    trustForwardHeader: true

此时认证服务收到的请求 Header 只有 Authorization 和 X-Test,而 Body 为空。

第二种:修改认证接口逻辑

由于认证接口可以忽略 Body,只需解析 Header,直接将 Parse() 改为 ParseHeaders()

// 认证接口入参解析
if err := httpx.ParseHeaders(r, &req); err != nil {
   httpx.ErrorCtx(r.Context(), w, err)
   return
}

虽然网关还是会把 Body 和 Header 全部转发过来,但是接口不做 Body 处理,就不会出现 Body 读取的问题了,而且无需为无用的 Body 分配内存。


网站公告

今日签到

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