当我尝试使用apl平台(管理组件)访问安全端点时找不到JWT令牌

I am trying to build my API with API Platform (Symfony 4) and everything seems to be fine but when I am using admin component to access secured endpoints (after I have been logged in successfully) server always returns 401 "JWT Token not found". I've found that Admin component is not sending the 'Authorization' header with token so server can't check token to authorize a request.

Since the Admin login works fine and token is always stored in localStorage (after successful login) I've tried to send a request with cURL and Postman with the stored token and server always returns valid response.

Any idea?

Update 1

I am using code from API Platform documentation (https://api-platform.com/docs/admin/authentication-support/#authentication-support).

App.js

import React from 'react';
import parseHydraDocumentation from '@api-platform/api-doc-parser/lib/hydra/parseHydraDocumentation';
import {fetchHydra as baseFetchHydra, HydraAdmin, hydraClient} from '@api-platform/admin';
import authProvider from './authProvider';
import {Redirect, Route} from 'react-router-dom';

const entrypoint = process.env.REACT_APP_API_ENTRYPOINT;
const fetchHeaders = {'Authorization': `Bearer ${localStorage.getItem('token')}`};
const fetchHydra = (url, options = {}) => baseFetchHydra(url, {
  ...options,
  headers: new Headers(fetchHeaders),
});
const dataProvider = api => hydraClient(api, fetchHydra);
const apiDocumentationParser = entrypoint =>
  parseHydraDocumentation(entrypoint, {
    headers: new Headers(fetchHeaders),
  }).then(
    ({api}) => ({api}),
    result => {
      const {api, status} = result;

      if (status === 401) {
        return Promise.resolve({
          api,
          status,
          customRoutes: [
            <Route path="/" render={() => <Redirect to="/login"/>}/>,
          ],
        });
      }

      return Promise.reject(result);
    }
  );

export default () => (
  <HydraAdmin
    apiDocumentationParser={apiDocumentationParser}
    authProvider={authProvider}
    entrypoint={entrypoint}
    dataProvider={dataProvider}
  />
);

authProvider.js

import {AUTH_CHECK, AUTH_ERROR, AUTH_LOGIN, AUTH_LOGOUT} from 'react-admin';

const authenticationTokenUri = `${process.env.REACT_APP_API_ENTRYPOINT}/authentication_token`;

export default (type, params) => {
  switch (type) {
    case AUTH_LOGIN:
      const {username, password} = params;
      const request = new Request(authenticationTokenUri, {
        method: 'POST',
        body: JSON.stringify({email: username, password}),
        headers: new Headers({'Content-Type': 'application/json'}),
      });

      return fetch(request)
        .then(response => {
          if (response.status < 200 || response.status >= 300) throw new Error(response.statusText);

          return response.json();
        })
        .then(({token}) => {
          localStorage.setItem('token', token); 
          window.location.replace('/');
        });

    case AUTH_LOGOUT:
      localStorage.removeItem('token');
      break;

    case AUTH_ERROR:
      if (401 === params.status || 403 === params.status) {
        localStorage.removeItem('token');

        return Promise.reject();
      }
      break;

    case AUTH_CHECK:
      return localStorage.getItem('token') ? Promise.resolve() : Promise.reject();

    default:
      return Promise.resolve();
  }
}

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));

serviceWorker.unregister();

Update 2

I have found that the authorization header with token is sent with some requests, but all requests to API endpoints are sent without authorization header that results in 401.

dev tools network img

correct request headers img

a endpoint request headers - miss authorization header img

dev tools console log img

Update 3

security.yaml

security:
    encoders:
        App\Entity\User:
            algorithm: bcrypt
    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: true
            stateless: true
            provider: app_user_provider
            json_login:
                check_path: /api/authentication_token
                username_path: email
                password_path: password
                success_handler: lexik_jwt_authentication.handler.authentication_success
                failure_handler: lexik_jwt_authentication.handler.authentication_failure
            guard:
                authenticators:
                    - lexik_jwt_authentication.jwt_token_authenticator

    access_control:
         # api platform has prefix /api
         - { path: ^/api/authentication_token$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
         - { path: ^/api/, roles: IS_AUTHENTICATED_FULLY } 

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)%' 

Greeting entity

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * This is a dummy entity. Remove it!
 *
 * @ApiResource
 * @ORM\Entity
 */
class Greeting
{
    /**
     * @var int The entity Id
     *
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @var string A nice person
     *
     * @ORM\Column
     * @Assert\NotBlank
     */
    public $name = '';

    public function getId(): int
    {
        return $this->id;
    }
}

Update 4

As you can see a request to a secured endpoint with Postman works. Postman printscreen

You need to set your Auth Header yourself. It won't be done by default.

const fetchHeaders = {'Authorization': `Bearer ${localStorage.getItem('token')}`};
const fetchHydra = (url, options = {}) => baseFetchHydra(url, {
    ...options,
    headers: new Headers(fetchHeaders),
});