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

401错误:JWT Token未找到,API Platform鉴权配置求助

解决API Platform中JWT Token not found的401错误

嘿,我之前也碰到过类似的JWT认证问题,结合你描述的场景,咱们一步步来排查解决:

一、先搞定前端Token的传递问题

错误提示“JWT Token not found”,首先要确认前端有没有把Token正确传给后端。Vue+axios的环境下,axios不会自动携带认证Token,得手动配置请求头:

  1. 给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);
});
  1. 检查跨域配置:如果前后端域名不同,要确保后端允许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.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 04:28:56