[BUG] AI proxy key mode can reuse a cached client with the wrong upstream API key
- Dominant language
- Java
- Stars
- 8.8k
- Forks
- 3.1k
- Avg merge
- 7d 1h
- Merged PRs (30d)
- 85
Description
### Current Behavior
When AI proxy key mode is enabled, the request path resolves the real upstream API key from the incoming `X-API-KEY` header and writes it into `primaryConfig`:
```java
final String realKey = AiProxyApiKeyCache.getInstance().getRealApiKey(selector.getId(), proxyApiKey);
if (Objects.nonNull(realKey)) {
primaryConfig.setApiKey(realKey);
}
```
But the main chat client cache key explicitly excludes `apiKey`:
```java
return Objects.hash(
config.getProvider(),
config.getBaseUrl(),
config.getModel(),
config.getTemperature(),
config.getMaxTokens(),
config.getStream()
// Explicitly exclude apiKey
);
```
The cached `ChatClient` is reused by selector/config hash, while the Spring AI model factory embeds the API key when the model is created. This means the first request that warms the cache can determine the upstream API key used by later requests with different proxy keys but the same selector/model settings.
### Expected Behavior
Proxy-key mode should not reuse a cached upstream client across different resolved real API keys, or the request-time credential should be injected in a way that is not captured by a shared cached model.
### Impact
Different tenants or callers using different Shenyu proxy keys can be routed to the wrong upstream AI account/key after the first cache warm-up. That can cause incorrect billing, quota usage, and authorization isolation issues.
### Code Location
- `shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-proxy/src/main/java/org/apache/shenyu/plugin/ai/proxy/enhanced/AiProxyPlugin.java`
- `proxyApiKey` is resolved and assigned to `primaryConfig.apiKey`.
- `generateConfigCacheKey()` excludes `apiKey`.
- `createMainChatClient()` reuses the cached client by selector/config hash.
- `shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/src/main/java/org/apache/shenyu/plugin/ai/common/spring/ai/factory/OpenAiModelFactory.java`
- `shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-common/src/main/java/org/apache/shenyu/plugin/ai/common/spring/ai/factory/DeepSeekModelFactory.java`
- factories build model instances with the API key from config.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start in AiProxyPlugin.java by tracing proxyApiKey resolution, generateConfigCacheKey(), and createMainChatClient(), then inspect OpenAiModelFactory.java and DeepSeekModelFactory.java to confirm where the API key is captured. Done means requests resolving different upstream keys cannot share a cached client, or the credential is no longer captured by a shared cached model.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, spring
- Domain
- api, authorization, backend, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100