为了记录,我使用PHP 7.0.0,在Vagrant Box中使用PHPStorm。哦,和Symfony 3。
我正在关注API Key Authentication文档。我的目标是:
apiKey
参数来验证任何路由,但开发人员分析器等除外$request->getUser()
以获取当前登录的用户我的问题是,虽然我相信我已经按照文档说明了这一点,但我仍然在控制器中获得null
$request->getUser()
。
注意:我已删除错误检查以保持代码简短
ApiKeyAuthenticator.php
处理从中获取API密钥的请求部分的事情。它可以是标题或任何内容,但我坚持使用GET中的
apiKey
。
与文档的差异,除了我试图在文档的this part之后的会话中保持用户身份验证之外,几乎为0。
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
自定义用户提供程序从可以加载的任何地方加载用户对象 - 我坚持使用默认的数据库实现。
差异:只是我必须将存储库注入构造函数以调用数据库,因为文档提到但未显示,并且还在$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
这是我的自定义用户对象。
我在这里唯一的区别是它包含学说注释(为了你的理智而删除)和令牌的自定义字段。此外,我删除了\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 --//
}
security.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
显然,我需要能够将存储库注入提供程序。
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
中,$user = $token->getUser();
中对authenticateToken()
的调用始终显示anon.
个用户,因此显然没有存储在会话中。
另请注意,在身份验证器的底部,我们确实返回了一个新的PreAuthenticatedToken
,其中包含从数据库中找到的用户:
所以它清楚地找到了我并返回它应该在这里的内容,但是控制器中的用户调用返回null
。我究竟做错了什么?由于我的自定义用户或其他东西,是否无法序列进入会话?我尝试将所有用户属性设置为公共文档中的某个位置,但没有区别。
答案 0 :(得分:2)
事实证明,在控制器中调用$request->getUser()
实际上并没有像我预期的那样返回当前经过身份验证的用户。这对于这个对象API imho来说是最有意义的。
如果您实际查看Request::getUser()
的代码,它看起来像这样:
/**
* Returns the user.
*
* @return string|null
*/
public function getUser()
{
return $this->headers->get('PHP_AUTH_USER');
}
这是HTTP Basic Auth!为了获得当前登录的用户,您需要每次都执行此操作:
$this->get('security.token_storage')->getToken()->getUser();
这确实给了我当前登录的用户。希望上面的问题显示了如何通过API令牌成功进行身份验证。
或者,不要调用$this->get()
,因为它是服务定位器。将自己与控制器分离并注入令牌服务,以便从中获取令牌和用户。
答案 1 :(得分:1)
要在控制器中获取当前登录的用户,只需致电:
$this->getUser();
这将引用Symfony的ControllerTrait中的一个方法,该方法基本上包含了Jimbo答案中提供的代码。
protected function getUser()
{
if (!$this->container->has('security.token_storage')) {
throw new \LogicException('The SecurityBundle is not registered in your application. Try running "composer require symfony/security-bundle".');
}
if (null === $token = $this->container->get('security.token_storage')->getToken()) {
return;
}
if (!is_object($user = $token->getUser())) {
// e.g. anonymous authentication
return;
}
return $user;
}