php - Symfony - 使用 API token 进行身份验证 - 请求 token 用户为空

标签 php symfony session authentication

For the record, I'm using PHP 7.0.0, in a Vagrant Box, with PHPStorm. Oh, and Symfony 3.

我正在关注 API Key Authentication文档。我的目标是:

  • 允许用户提供 key 作为 GET apiKey 参数以对任何 路由进行身份验证,显然开发人员分析器等除外
  • 允许开发人员在 Controller 中编写 $request->getUser() 以获取当前登录的用户

我的问题是,尽管我相信我已经严格按照文档进行操作,但对于 $request->getUser(),我仍然得到一个 null在 Controller 中。

注意:我删除了错误检查以保持代码简短

ApiKeyAuthenticator.php

The thing that processes the part of the request to grab the API key from it. It can be a header or anything, but I'm sticking with apiKey from GET.

与文档的差异,除此之外几乎为 0 我试图在 this part 之后的 session 中对用户进行身份验证的文档。

class ApiKeyAuthenticator implements SimplePreAuthenticatorInterface
{
    public function createToken(Request $request, $providerKey)
    {
        $apiKey = $request->query->get('apiKey');

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

    public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
    {
        $apiKey = $token->getCredentials();
        $username = $userProvider->getUsernameForApiKey($apiKey);

        // The part where we try and keep the user in the session!
        $user = $token->getUser();
        if ($user instanceof ApiKeyUser) {
            return new PreAuthenticatedToken(
                $user,
                $apiKey,
                $providerKey,
                $user->getRoles()
            );
        }


        $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;
    }
}

ApiKeyUserProvider.php

The custom user provider to load a user object from wherever it can be loaded from - I'm sticking with the default DB implementation.

区别:唯一的事实是我必须将存储库注入(inject)构造函数以调用数据库,正如文档提到但没有显示的那样,并且还在 $user 中返回 refreshUser()

class ApiKeyUserProvider implements UserProviderInterface
{
    protected $repo;

    // I'm injecting the Repo here (docs don't help with this)
    public function __construct(UserRepository $repo)
    {
        $this->repo = $repo;
    }

    public function getUsernameForApiKey($apiKey)
    {
        $data = $this->repo->findUsernameByApiKey($apiKey);

        $username = (!is_null($data)) ? $data->getUsername() : null;

        return $username;
    }

    public function loadUserByUsername($username)
    {
        return $this->repo->findOneBy(['username' => $username]);
    }

    public function refreshUser(UserInterface $user)
    {
        // docs state to return here if we don't want stateless
        return $user;
    }

    public function supportsClass($class)
    {
        return 'Symfony\Component\Security\Core\User\User' === $class;
    }
}

ApiKeyUser.php

This is my custom user object.

我这里唯一的区别是它包含学说注释(为了您的理智而删除)和 token 的自定义字段。此外,我删除了 \Serializable 因为它似乎没有做任何事情,显然 Symfony 只需要 $id 值来重新创建它可以自己做的用户。

class ApiKeyUser implements UserInterface
{
    private $id;
    private $username;
    private $password;
    private $email;
    private $salt;
    private $apiKey;
    private $isActive;

    public function __construct($username, $password, $salt, $apiKey, $isActive = true)
    {
        $this->username = $username;
        $this->password = $password;
        $this->salt = $salt;
        $this->apiKey = $apiKey;
        $this->isActive = $isActive;
    }

    //-- SNIP getters --//
}

安全.yml

# Here is my custom user provider class from above
providers:
    api_key_user_provider:
        id: api_key_user_provider

firewalls:
    # Authentication disabled for dev (default settings)
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false
    # My new settings, with stateless set to false
    secured_area:
        pattern: ^/
        stateless: false
        simple_preauth:
            authenticator: apikey_authenticator
        provider:
            api_key_user_provider

services.yml

显然,我需要能够将存储库注入(inject)提供程序。

api_key_user_repository:
    class: Doctrine\ORM\EntityRepository
    factory: ["@doctrine.orm.entity_manager", getRepository]
    arguments: [AppBundle\Security\ApiKeyUser]

api_key_user_provider:
    class:  AppBundle\Security\ApiKeyUserProvider
    factory_service: doctrine.orm.default_entity_manager
    factory_method: getRepository
    arguments: ["@api_key_user_repository"]

apikey_authenticator:
    class: AppBundle\Security\ApiKeyAuthenticator
    public: false

调试。有趣的是,在 ApiKeyAuthenticator.php 中,在 authenticateToken() 中调用了 $user = $token->getUser();总是显示一个 anon. 用户,所以它显然没有存储在 session 中。

Debug 1 Debug 2

另请注意,在身份验证器的底部,我们实际上是如何返回一个新的 PreAuthenticatedToken 以及从数据库中找到的用户:

Debug 3 Debug 4

所以很明显它找到了我并返回了它应该返回的内容,但是 Controller 中的用户调用返回了 null。我究竟做错了什么?由于我的自定义用户或其他原因,无法序列化到 session 中吗?我尝试按照文档中建议的某处将所有用户属性设置为公共(public)属性,但这没有任何区别。

最佳答案

事实证明,在 Controller 中调用 $request->getUser() 实际上并没有像我预期的那样返回当前经过身份验证的用户。这对于这个对象 API 恕我直言最有意义。

如果您实际查看 Request::getUser() 的代码,它看起来像这样:

/**
 * Returns the user.
 *
 * @return string|null
 */
public function getUser()
{
    return $this->headers->get('PHP_AUTH_USER');
}

这是针对 HTTP 基本身份验证的!为了获取当前登录的用户,您需要每次都这样做:

$this->get('security.token_storage')->getToken()->getUser();

确实,这确实为我提供了当前登录的用户。希望上面的问题展示了如何通过 API token 成功进行身份验证。

或者,不要调用 $this->get(),因为它是一个服务定位器。将自己与 Controller 分离并注入(inject) token 服务,而不是从中获取 token 和用户。

关于php - Symfony - 使用 API token 进行身份验证 - 请求 token 用户为空,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35480550/

相关文章:

php - 如何使用另一个数组的值在 php 数组中动态注入(inject)键?

PHP: fatal error 调用成员函数......在非对象上

php - Symfony2 表单在页面刷新时提交

php - 如何在 monolog symfony2 日志记录中禁用 security.info 和 security.debug

Java Soap Glassfish 登录后获取 session

python - 如何在 python 中添加标题以休眠?

asp.net - 提供对 session 对象的强类型访问

php - Laravel sqlite 数据库连接错误

symfony - 在 Symfony 2 中导入 CSV

php - 图片上传在 Linkedin V2 API 中不起作用