【发布时间】:2016-05-30 13:33:33
【问题描述】:
作为记录,我在 Vagrant Box 中使用 PHP 7.0.0 和 PHPStorm。哦,还有 Symfony 3。
我正在关注API Key Authentication 文档。我的目标是:
- 允许用户提供密钥作为 GET
apiKey参数以对 任何 路由进行身份验证,显然开发者分析器等除外 - 允许开发者在控制器中写入
$request->getUser()来获取当前登录的用户
我的问题是,尽管我相信我已经完全按照文档进行操作,但我仍然在控制器中为 $request->getUser() 获得了一个 null。
注意:我已删除错误检查以保持代码简短
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
自定义用户提供程序,用于从可以加载用户对象的任何位置加载用户对象 - 我坚持使用默认的 DB 实现。
差异:只有我必须将存储库注入构造函数才能调用数据库这一事实,正如文档所暗示但未显示的那样,并且还在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 中,对authenticateToken() 中的$user = $token->getUser(); 的调用总是显示anon. 用户,因此它显然没有存储在会话中。
还要注意,在身份验证器的底部,我们实际上是如何返回一个新的PreAuthenticatedToken,其中包含从数据库中找到的用户:
所以它很明显找到了我并且正在返回它应该在这里,但是控制器中的用户调用返回null。我究竟做错了什么?由于我的自定义用户或其他原因,无法序列化到会话中吗?我尝试按照文档建议的某处将所有用户属性设置为公开,但这没有任何区别。
【问题讨论】:
-
你可以试试这个,添加到你的防火墙:
access_control: - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY } -
我在
security.yml的security下添加了这个键。没有变化。 -
您可以尝试将 ApiKeyUser 类中的所有属性设置为受保护吗?
-
@hasumedic 我已经尝试了
public和protected访问修饰符,用户仍然是null。 -
嗯,那你能不能试着改变你的 ApiKeyUserProvider::supportsClass 的实现为'return ApiKeyUser::class=== $class;'?
标签: php symfony session authentication