Symfony3:在TokenStorage中找不到令牌:AuthenticationCredentialsNotFoundException

时间:2018-01-06 23:54:09

标签: php-7 symfony-3.4

我关注了Symfony doc。教程here并在我的应用程序中开发了API密钥认证机制。 我的security.yml如下所示,我需要链提供商回退到http basic auth,以防用户找不到API密钥。

security:
    encoders:
        FOS\UserBundle\Model\UserInterface: sha512
        Symfony\Component\Security\Core\User\User:
            algorithm: bcrypt
            encode_as_base64: false
            iterations: 4

role_hierarchy:
    ROLE_ADMIN:       ROLE_USER
    ROLE_SUPER_ADMIN: ROLE_ADMIN

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]

firewalls:
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false

    user:
        pattern: ^/users/
        anonymous: true
        stateless: true
        logout: true

    api:
        anonymous: false
        stateless: true
        simple_preauth:
            authenticator: AppBundle\Security\ApiKeyAuthenticator
        provider: chain_provider

当我尝试访问系统的安全区域(' api')时,会抛出上述错误。我对Symfony防火墙配置很安静,有人可以告诉我这方面的正确方向吗?

我的ApiKeyUserProvider课程如下:

class ApiKeyUserProvider implements UserProviderInterface
{
    /** @var EntityManager $entityManager */
    protected $entityManager;

    public function __construct(EntityManager $entityManager)
    {
        $this->entityManager = $entityManager;
    }

    /**
     * Get the username of a user for a given API key
     *
     * @param $apiKey
     * @return string|null
     */
    public function getUsernameForApiKey($apiKey)
    {
        $apiKeyRepository = $this->entityManager->getRepository('UserBundle:ApiKey');

        $username = $apiKeyRepository->getUserNameForApiKey($apiKey);

        return $username;
    }

    /**
     * Loads the user for the given username.
     *
     * This method must throw UsernameNotFoundException if the user is not
     * found.
     *
     * @param string $username The username
     *
     * @return UserInterface
     *
     * @throws UsernameNotFoundException if the user is not found
     */
    public function loadUserByUsername($username)
    {
        $userRepository = $this->entityManager->getRepository('UserBundle:User');

        $user = $userRepository->findOneByUsername($username);

        if (!$user instanceof User) {

            throw new UsernameNotFoundException();
        }

        return $user;
    }

    /**
     * Refreshes the user.
     *
     * It is up to the implementation to decide if the user data should be
     * totally reloaded (e.g. from the database), or if the UserInterface
     * object can just be merged into some internal array of users / identity
     * map.
     *
     * @return UserInterface
     *
     * @throws UnsupportedUserException if the user is not supported
     */
    public function refreshUser(UserInterface $user)
    {
        // this is used for storing authentication in the session
        // but in this case, the token is sent in each request,
        // so authentication can be stateless. Throwing this exception
        // is proper to make things stateless
        throw new UnsupportedUserException();
    }

    /**
     * Whether this provider supports the given user class.
     *
     * @param string $class
     *
     * @return bool
     */
    public function supportsClass($class)
    {
        return User::class === $class;
    }
}

我的ApiKeyAuthenticator课程如下所示:

class ApiKeyAuthenticator implements SimplePreAuthenticatorInterface
{
    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) {
            // CAUTION: this message will be returned to the client
            // (so don't put any un-trusted messages / error strings here)
            throw new CustomUserMessageAuthenticationException(
                sprintf('API Key "%s" does not exist.', $apiKey)
            );
        }

        $user = $userProvider->loadUserByUsername($username);

        return new PreAuthenticatedToken(
            $user,
            $apiKey,
            $providerKey,
            $user->getRoles()
        );
    }

    public function supportsToken(TokenInterface $token, $providerKey)
    {
        return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
    }

    public function createToken(Request $request, $providerKey)
    {
        // look for an apikey query parameter
//        $apiKey = $request->query->get('apikey');

        // or if you want to use an "apikey" header, then do something like this:
        $apiKey = $request->headers->get('apikey');
        if (!$apiKey) {
            // throw new BadCredentialsException();

            // or to just skip api key authentication
            return null;
        }

        return new PreAuthenticatedToken(
            'anon.',
            $apiKey,
            $providerKey
        );
    }
}

0 个答案:

没有答案