You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Symfony3 TokenStorage无令牌报错:API密钥认证转HTTP Basic Auth配置问题

我来帮你梳理下这个问题,你遇到的AuthenticationCredentialsNotFoundException主要是因为当前的API防火墙配置只处理了API Key认证,而且在认证失败时没有正确触发降级到HTTP Basic Auth的流程。下面是具体的解决思路和修改步骤:

问题核心分析

你的需求是实现「API Key认证失败时自动降级到HTTP Basic Auth」,但当前配置存在两个关键问题:

  1. 防火墙仅启用了API Key认证器:api防火墙只配置了simple_preauth,没有添加HTTP Basic Auth的支持,导致无API Key或API Key无效时,没有其他认证方式可以尝试。
  2. API Key认证失败直接终止流程:你的ApiKeyAuthenticator在API Key无效时抛出CustomUserMessageAuthenticationException,这会直接中断认证流程,不会触发后续的认证降级逻辑。
具体解决步骤

1. 更新security.yml防火墙配置

在api防火墙中同时添加simple_preauth和http_basic,认证顺序很重要——先尝试API Key,失败后再降级到HTTP Basic。同时确保链式提供者正确关联:

security:
    # ... 其他配置(encoders、role_hierarchy、providers)保持不变 ...
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        user:
            pattern: ^/users/
            anonymous: true
            stateless: true
            logout: true
        api:
            anonymous: false
            stateless: true
            # 优先尝试API Key认证
            simple_preauth:
                authenticator: AppBundle\Security\ApiKeyAuthenticator
            # API Key失败时降级到HTTP Basic Auth
            http_basic: ~
            provider: chain_provider

2. 修改ApiKeyAuthenticator逻辑

调整两个关键方法,让认证失败时允许流程继续,触发后续的HTTP Basic Auth:

class ApiKeyAuthenticator implements SimplePreAuthenticatorInterface
{
    public function createToken(Request $request, $providerKey)
    {
        $apiKey = $request->headers->get('apikey');

        if (!$apiKey) {
            // 无API Key时返回null,让防火墙自动尝试下一个认证方式
            return null;
        }

        return new PreAuthenticatedToken(
            'anon.',
            $apiKey,
            $providerKey
        );
    }

    public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
    {
        if (!$userProvider instanceof ApiKeyUserProvider) {
            throw new \InvalidArgumentException(
                sprintf(
                    'The user provider must be an instance of ApiKeyUserProvider (%s was given).',
                    get_class($userProvider)
                )
            );
        }

        $apiKey = $token->getCredentials();
        $username = $userProvider->getUsernameForApiKey($apiKey);

        if (!$username) {
            // API Key无效时抛出通用的BadCredentialsException,让防火墙继续尝试下一个认证器
            // 注意:不要用CustomUserMessageAuthenticationException,它会直接返回错误响应中断流程
            throw new BadCredentialsException();
        }

        $user = $userProvider->loadUserByUsername($username);

        return new PreAuthenticatedToken(
            $user,
            $apiKey,
            $providerKey,
            $user->getRoles()
        );
    }

    // ... supportsToken方法保持不变 ...
}

3. 验证链式提供者配置

你的链式提供者配置已经正确:

providers:
    api_key_user_provider:
        id: AppBundle\Security\ApiKeyUserProvider
    fos_userbundle:
        id: fos_user.user_provider.username
    chain_provider:
        chain:
            providers: [api_key_user_provider, fos_userbundle]

只需确保api防火墙的provider指向chain_provider即可(已经在步骤1中配置)。

额外测试建议

修改完成后,可以通过以下场景验证:

  • 发送带有效apikey请求头的请求:应该通过API Key认证成功。
  • 发送带无效apikey请求头的请求:应该自动触发HTTP Basic Auth验证。
  • 不发送apikey请求头的请求:直接触发HTTP Basic Auth验证。

内容的提问来源于stack exchange,提问作者ssrp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:42:14