Symfony 6.2使用AccessToken无法完成用户认证求助
问题描述
基于Symfony 6.2构建API应用,采用AccessToken实现无状态认证,已按官方文档实现AccessTokenHandler类并配置security.yaml。数据库中存在有效用户及关联令牌,请求可正常进入AccessTokenHandler::getUserBadgeFrom方法,且能返回正确的令牌及关联用户,但请求始终返回401 Unauthorized状态码,响应头包含WWW-Authenticate: Bearer error="invalid_token",error_description="Invalid credentials."。
现有代码
AccessTokenHandler类
<?php # src\Security\AccessTokenHandler.php namespace App\Security; use App\Repository\AccessTokenRepository; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; class AccessTokenHandler implements AccessTokenHandlerInterface { public function __construct( private AccessTokenRepository $repository ) { } public function getUserBadgeFrom( string $accessToken ): UserBadge { // 查询数据库验证令牌 $accessToken = $this->repository->findValidToken($accessToken); if ( NULL === $accessToken || !$accessToken->isValid() ) { throw new BadCredentialsException( 'Invalid credentials.' ); } // 返回包含用户标识的UserBadge return new UserBadge( $accessToken->getUserId() ); } }
security.yaml配置
security: # 用户密码哈希配置 password_hashers: Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' # 用户提供者配置 providers: access_token_provider: entity: class: App\Entity\AccessToken property: tokenValue firewalls: main: lazy: true provider: access_token_provider stateless: true pattern: ^/api access_token: token_extractors: header token_handler: App\Security\AccessTokenHandler access_control: - { path: ^/api, roles: ROLE_ADMIN }
问题原因
用户提供者配置错误:当前配置的access_token_provider是针对AccessToken实体的,但AccessTokenHandler返回的UserBadge携带的是用户ID,Symfony需要通过用户提供者加载对应的User实体而非AccessToken实体,导致无法匹配到有效用户,最终认证失败。
解决方案
1. 修改security.yaml中的用户提供者配置
将用户提供者改为针对User实体,并确保防火墙使用该提供者:
security: password_hashers: Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' providers: # 替换为User实体的提供者 app_user_provider: entity: class: App\Entity\User property: id # 与UserBadge返回的用户ID匹配 firewalls: main: lazy: true provider: app_user_provider # 使用新的用户提供者 stateless: true pattern: ^/api access_token: token_extractors: header token_handler: App\Security\AccessTokenHandler access_control: - { path: ^/api, roles: ROLE_ADMIN }
2. 确保User实体符合要求
你的User实体必须实现Symfony\Component\Security\Core\User\UserInterface接口,包含getRoles()、getUserIdentifier()等必要方法;若涉及密码认证,还需实现PasswordAuthenticatedUserInterface接口。
3. 验证用户ID的有效性
确认AccessToken::getUserId()返回的是User实体的有效ID,且数据库中存在对应的用户记录。
内容的提问来源于stack exchange,提问作者Denis O.

