下面是我的安全配置:
security:
firewalls:
secured_area:
pattern: ^/r
stateless: true
simple_preauth:
authenticator: apikey_authenticator
像/r/companies/1
和/r/news/2
这样的资源需要被我的Backbone.js应用程序访问,而不需要一个apikey与GET请求一起发送。对于POST
, DELETE
和PATCH
请求,处理改变资源的状态,一个apikey必须随请求一起发送,用户需要登录,并获得登录的机会,而不是通过403状态码的情况下。
现在对资源的所有请求都触发预认证过程,问题是我无法配置它让匿名用户通过。这只是一个测试,下面是代码(基于教程如何使用API密钥验证用户):
class ApiKeyAuthenticator implements SimplePreAuthenticatorInterface, AuthenticationSuccessHandlerInterface
{
protected $userProvider;
protected $httpUtils;
public function __construct(UserProviderInterface $userProvider, HttpUtils $httpUtils)
{
$this->userProvider = $userProvider;
$this->httpUtils = $httpUtils;
}
public function createToken(Request $request, $providerKey)
{
$targetUrl = '/r/login/check';
if ($this->httpUtils->checkRequestPath($request, $targetUrl)){
if (!$request->query->has('email')) {
throw new BadCredentialsException('Credentials not correct or not present');
}
if (!$request->query->has('password')) {
throw new BadCredentialsException('Credentials not correct or not present');
}
if($request->query->get('email') === 'testemail' && $request->query->get('password') === 'testpassword'){
$token = 12345;
}
}else{
if (!$request->query->has('apikey')) {
$token = '';
}else{
$token = $request->query->get('apikey');
}
}
return new PreAuthenticatedToken( //what is the meaning of this class?
'anon.',
$token,
$providerKey
);
}
public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
{
$apiKey = $token->getCredentials();
$username = $this->userProvider->getUsernameForApiKey($apiKey);
if (!$username) {
throw new AuthenticationException("Apikey not found");
}
$user = $this->userProvider->loadUserByUsername($username);
return new PreAuthenticatedToken(
$user,
$apiKey,
$providerKey,
$user->getRoles()
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$targetUrl = '/r/login/check';
if ($this->httpUtils->checkRequestPath($request, $targetUrl)){
$response = new Response();
$response->setStatusCode(Response::HTTP_OK);
$response->headers->set('apikey', '12345');
return $response;
}
}
public function supportsToken(TokenInterface $token, $providerKey)
{
return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
}
}
现在这段代码不适用于没有apikey
查询参数的GET请求,因为在这种情况下,$token = ''
将从getUsernameForApiKey()
返回,因此不会返回$user
如何处理这种情况?我宁愿GET请求^/r不进入预认证过程(我不知道如何做到这一点)。但如果他们这样做了,我怎么能让匿名用户通过呢?现在我一直收到A Token was not found in the SecurityContext. (500 Internal Server Error)
。代码显然是不完整的,但那是因为我不知道如何继续。
在authenticateToken()
中使用AnonymousToken
类解决了我的问题。虽然我仍然不知道这是否安全,或者是实现我想要的最好的方法。
$apiKey = $token->getCredentials();
if($apiKey == ''){
//request without a token
return new AnonymousToken('anon.','anonymous',array('IS_AUTHENTICATED_ANONYMOUSLY'));
}
下面是AnonymousToken类的Symfony API引用