401错误:JWT Token未找到,API Platform鉴权配置求助
解决API Platform中JWT Token not found的401错误
嘿,我之前也碰到过类似的JWT认证问题,结合你描述的场景,咱们一步步来排查解决:
一、先搞定前端Token的传递问题
错误提示“JWT Token not found”,首先要确认前端有没有把Token正确传给后端。Vue+axios的环境下,axios不会自动携带认证Token,得手动配置请求头:
- 给axios添加全局请求拦截器,确保每次请求都带上Token:
// 比如在Vue项目的main.js里配置 axios.interceptors.request.use(config => { // 假设你把登录后拿到的Token存在了localStorage里 const token = localStorage.getItem('jwt_token'); if (token) { // 注意格式必须是 `Bearer <你的Token>` config.headers.Authorization = `Bearer ${token}`; } return config; }, error => { return Promise.reject(error); });
- 检查跨域配置:如果前后端域名不同,要确保后端允许
Authorization头跨域传递。API Platform后端可以通过Symfony的CORS Bundle或者Nginx配置来开启这个权限。
二、后端security.yaml配置校验(基于官方文档版本)
你提到第二个配置是遵循官方文档的,那得确保配置里的关键项没出错,给你个参考示例:
security: # Symfony 5.4+必须开启这个,启用认证管理器 enable_authenticator_manager: true providers: # 自定义用户提供者,替换成你自己的服务ID app_user_provider: id: App\Security\UserProvider firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: lazy: true provider: app_user_provider # JWT是无状态认证,这个必须设为true stateless: true jwt: # 指定自定义的JWT认证器 authenticator: App\Security\JwtAuthenticator # 或者用默认的Token提取器,确保能从Authorization头取到Token # token_extractors: # authorization_header: # enabled: true # prefix: Bearer access_control: # 登录接口允许匿名访问 - { path: ^/api/login, roles: PUBLIC_ACCESS } # 其他API接口需要完整认证 - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
三、实现自定义用户提供者和JWT认证器
按照官方文档的要求,这两个文件是必须的,给你写个基础实现:
1. 用户提供者(App\Security\UserProvider)
<?php namespace App\Security; use App\Entity\User; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; class UserProvider implements UserProviderInterface { public function __construct(private EntityManagerInterface $em) {} public function loadUserByIdentifier(string $identifier): UserInterface { // 这里根据你的用户实体字段调整,比如用email或者用户名作为标识符 $user = $this->em->getRepository(User::class)->findOneBy(['email' => $identifier]); if (!$user) { throw new UserNotFoundException(sprintf('用户 "%s" 不存在', $identifier)); } return $user; } public function refreshUser(UserInterface $user): UserInterface { // 无状态认证下,直接返回用户即可 return $user; } public function supportsClass(string $class): bool { return User::class === $class || is_subclass_of($class, User::class); } }
2. JWT认证器(App\Security\JwtAuthenticator)
<?php namespace App\Security; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException; use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Passport; use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport; use Lexik\Bundle\JWTAuthenticationBundle\Decoder\JWTTokenDecoderInterface; class JwtAuthenticator extends AbstractAuthenticator { public function __construct(private JWTTokenDecoderInterface $jwtDecoder) {} public function supports(Request $request): ?bool { return $request->headers->has('Authorization'); } public function authenticate(Request $request): Passport { $authHeader = $request->headers->get('Authorization'); if (null === $authHeader) { throw new CustomUserMessageAuthenticationException('JWT Token not found'); } // 提取Bearer后面的Token内容 $token = str_replace('Bearer ', '', $authHeader); // 解析Token获取用户标识符(比如email) $payload = $this->jwtDecoder->decode($token); if (!$payload) { throw new CustomUserMessageAuthenticationException('无效的JWT Token'); } return new SelfValidatingPassport( new UserBadge($payload['email']) ); } public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response { // 认证成功不需要额外处理,继续执行请求 return null; } public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response { return new JsonResponse( ['message' => $exception->getMessage()], Response::HTTP_UNAUTHORIZED ); } }
另外别忘了确保已经安装并配置了LexikJWTAuthenticationBundle,密钥对的配置要正确:
# config/packages/lexik_jwt_authentication.yaml lexik_jwt_authentication: secret_key: '%env(resolve:JWT_SECRET_KEY)%' public_key: '%env(resolve:JWT_PUBLIC_KEY)%' pass_phrase: '%env(JWT_PASSPHRASE)%'
四、额外排查小技巧
- 用Chrome开发者工具的Network标签,查看请求的Headers,确认Authorization头确实发送了,格式也正确
- 去https://jwt.io/解析你的Token,检查payload是否正确、是否过期
- 查看Symfony的
var/log/dev.log,里面会有更详细的认证失败细节,比如签名无效、Token过期等
内容的提问来源于stack exchange,提问作者Dmitry S.
相关产品推荐
相关产品推荐

