From f2fcd68ef8f710b7c21189985e7a5249aec673aa Mon Sep 17 00:00:00 2001 From: zikunchang <89329967+changsci@users.noreply.github.com> Date: Wed, 28 Jan 2026 14:03:24 +0800 Subject: [PATCH] feature: Support getting the API key from the request header when provider.apiTokens is not configured. (#3394) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 澄潭 --- .../extensions/ai-proxy/provider/openai.go | 58 ++++++++++++++++++- .../extensions/ai-proxy/provider/provider.go | 3 + 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/plugins/wasm-go/extensions/ai-proxy/provider/openai.go b/plugins/wasm-go/extensions/ai-proxy/provider/openai.go index 746015267..a96085efc 100644 --- a/plugins/wasm-go/extensions/ai-proxy/provider/openai.go +++ b/plugins/wasm-go/extensions/ai-proxy/provider/openai.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/alibaba/higress/plugins/wasm-go/extensions/ai-proxy/util" + "github.com/higress-group/proxy-wasm-go-sdk/proxywasm" "github.com/higress-group/proxy-wasm-go-sdk/proxywasm/types" "github.com/higress-group/wasm-go/pkg/log" "github.com/higress-group/wasm-go/pkg/wrapper" @@ -134,8 +135,63 @@ func (m *openaiProvider) TransformRequestHeaders(ctx wrapper.HttpContext, apiNam } else { util.OverwriteRequestHostHeader(headers, defaultOpenaiDomain) } + + var token string + + // 1. If apiTokens is configured, use it first if len(m.config.apiTokens) > 0 { - util.OverwriteRequestAuthorizationHeader(headers, "Bearer "+m.config.GetApiTokenInUse(ctx)) + token = m.config.GetApiTokenInUse(ctx) + if token == "" { + log.Warnf("[openaiProvider.TransformRequestHeaders] apiTokens count > 0 but GetApiTokenInUse returned empty") + } + } else { + // If no apiToken is configured, try to extract from original request headers + + // 2. If authHeaderKey is configured, use the specified header + if m.config.authHeaderKey != "" { + if apiKey, err := proxywasm.GetHttpRequestHeader(m.config.authHeaderKey); err == nil && apiKey != "" { + token = apiKey + log.Debugf("[openaiProvider.TransformRequestHeaders] Using token from configured header: %s", m.config.authHeaderKey) + } + } + + // 3. If authHeaderKey is not configured, check default headers in priority order + if token == "" { + defaultHeaders := []string{"x-api-key", "x-authorization"} + for _, headerName := range defaultHeaders { + if apiKey, err := proxywasm.GetHttpRequestHeader(headerName); err == nil && apiKey != "" { + token = apiKey + log.Debugf("[openaiProvider.TransformRequestHeaders] Using token from %s header", headerName) + break + } + } + } + + // 4. Finally check Authorization header + if token == "" { + if auth, err := proxywasm.GetHttpRequestHeader("Authorization"); err == nil && auth != "" { + // Extract token from "Bearer " format + if strings.HasPrefix(auth, "Bearer ") { + token = strings.TrimPrefix(auth, "Bearer ") + log.Debugf("[openaiProvider.TransformRequestHeaders] Using token from Authorization header (Bearer format)") + } else { + token = auth + log.Debugf("[openaiProvider.TransformRequestHeaders] Using token from Authorization header (no Bearer prefix)") + } + } + } + } + + // 5. Set Authorization header (avoid duplicate Bearer prefix) + if token != "" { + // Check if token already contains Bearer prefix + if !strings.HasPrefix(token, "Bearer ") { + token = "Bearer " + token + } + util.OverwriteRequestAuthorizationHeader(headers, token) + log.Debugf("[openaiProvider.TransformRequestHeaders] Set Authorization header successfully") + } else { + log.Warnf("[openaiProvider.TransformRequestHeaders] No auth token available - neither configured in apiTokens nor in request headers") } headers.Del("Content-Length") } diff --git a/plugins/wasm-go/extensions/ai-proxy/provider/provider.go b/plugins/wasm-go/extensions/ai-proxy/provider/provider.go index ef71d6d98..d0983402a 100644 --- a/plugins/wasm-go/extensions/ai-proxy/provider/provider.go +++ b/plugins/wasm-go/extensions/ai-proxy/provider/provider.go @@ -397,6 +397,9 @@ type ProviderConfig struct { // @Title zh-CN 指定服务返回的响应需满足的JSON Schema // @Description zh-CN 目前仅适用于OpenAI部分模型服务。参考:https://platform.openai.com/docs/guides/structured-outputs responseJsonSchema map[string]interface{} `required:"false" yaml:"responseJsonSchema" json:"responseJsonSchema"` + // @Title zh-CN 自定义认证Header名称 + // @Description zh-CN 用于从请求中提取认证token的自定义header名称。如不配置,则按默认优先级检查 x-api-key、x-authorization、anthropic-api-key 和 Authorization header。 + authHeaderKey string `required:"false" yaml:"authHeaderKey" json:"authHeaderKey"` // @Title zh-CN 自定义大模型参数配置 // @Description zh-CN 用于填充或者覆盖大模型调用时的参数 customSettings []CustomSetting