Symfony3 TokenStorage无令牌报错:API密钥认证转HTTP Basic Auth配置问题
我来帮你梳理下这个问题,你遇到的AuthenticationCredentialsNotFoundException主要是因为当前的API防火墙配置只处理了API Key认证,而且在认证失败时没有正确触发降级到HTTP Basic Auth的流程。下面是具体的解决思路和修改步骤:
问题核心分析
你的需求是实现「API Key认证失败时自动降级到HTTP Basic Auth」,但当前配置存在两个关键问题:
- 防火墙仅启用了API Key认证器:
api防火墙只配置了simple_preauth,没有添加HTTP Basic Auth的支持,导致无API Key或API Key无效时,没有其他认证方式可以尝试。 - 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
相关产品推荐
相关产品推荐

