[英]Symfony - Authentication with an API Token - Request token user is null
為了記錄,我使用PHP 7.0.0,在Vagrant Box中使用PHPStorm。 哦,和Symfony 3。
我正在關注API密鑰身份驗證文檔。 我的目標是:
apiKey
參數來驗證任何路由,顯然除了開發人員分析器等 $request->getUser()
以獲取當前登錄的用戶 我的問題是,雖然我相信我已經遵循了文檔中的文檔,但我仍然在控制器中獲取$request->getUser()
的null
。
注意:我已刪除錯誤檢查以保持代碼簡短
ApiKeyAuthenticator.php
處理從中獲取API密鑰的請求部分的事情。 它可以是標題或任何東西,但我堅持使用GET中的
apiKey
。
與文檔的差異,除了我試圖在這部分文檔之后的會話中保持用戶身份驗證之外,幾乎為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
自定義用戶提供程序從可以加載的任何地方加載用戶對象 - 我堅持使用默認的數據庫實現。
差異:只有這樣一個事實:我必須將存儲庫注入構造函數以調用數據庫,因為文檔提示但不顯示,並且還在refreshUser()
返回$user
。
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
。 我究竟做錯了什么? 由於我的自定義用戶或其他東西,是否無法序列進入會話? 我嘗試將所有用戶屬性設置為公共,因為在文檔建議的某處,但沒有區別。
所以事實證明,在控制器中調用$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()
因為它是服務定位器。 將自己與控制器分離並注入令牌服務,以便從中獲取令牌和用戶。
要在Controller中獲取當前登錄的用戶,只需致電:
$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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.