如何让没有API密钥(匿名用户)的预认证GET请求通过


How to let pre authentication GET requests without an API key(anonymous users) pass?

下面是我的安全配置:

security:
    firewalls:
      secured_area:
          pattern: ^/r
          stateless: true
          simple_preauth:
              authenticator: apikey_authenticator

/r/companies/1/r/news/2这样的资源需要被我的Backbone.js应用程序访问,而不需要一个apikey与GET请求一起发送。对于POST, DELETEPATCH请求,处理改变资源的状态,一个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引用