我关注了 Symfony 文档。教程在这里并在我的应用程序中开发了 API 密钥身份验证机制。我的security.yml
样子如下,我需要链提供者回退到 http 基本身份验证,以防找不到使用 API 密钥的用户。
security:
encoders:
FOS\UserBundle\Model\UserInterface: sha512
Symfony\Component\Security\Core\User\User:
algorithm: bcrypt
encode_as_base64: false
iterations: 4
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
api_key_user_provider:
id: AppBundle\Security\ApiKeyUserProvider
fos_userbundle:
id: fos_user.user_provider.username
chain_provider:
chain:
providers: [api_key_user_provider, fos_userbundle]
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
user:
pattern: ^/users/
anonymous: true
stateless: true
logout: true
api:
anonymous: false
stateless: true
simple_preauth:
authenticator: AppBundle\Security\ApiKeyAuthenticator
provider: chain_provider
当我尝试访问系统的安全区域('api')时,它会引发上述错误。我对 Symfony 防火墙配置很陌生,有人可以告诉我正确的方向吗?
我的ApiKeyUserProvider
课程如下所示:
class ApiKeyUserProvider implements UserProviderInterface
{
/** @var EntityManager $entityManager */
protected $entityManager;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* Get the username of a user for a given API key
*
* @param $apiKey
* @return string|null
*/
public function getUsernameForApiKey($apiKey)
{
$apiKeyRepository = $this->entityManager->getRepository('UserBundle:ApiKey');
$username = $apiKeyRepository->getUserNameForApiKey($apiKey);
return $username;
}
/**
* Loads the user for the given username.
*
* This method must throw UsernameNotFoundException if the user is not
* found.
*
* @param string $username The username
*
* @return UserInterface
*
* @throws UsernameNotFoundException if the user is not found
*/
public function loadUserByUsername($username)
{
$userRepository = $this->entityManager->getRepository('UserBundle:User');
$user = $userRepository->findOneByUsername($username);
if (!$user instanceof User) {
throw new UsernameNotFoundException();
}
return $user;
}
/**
* Refreshes the user.
*
* It is up to the implementation to decide if the user data should be
* totally reloaded (e.g. from the database), or if the UserInterface
* object can just be merged into some internal array of users / identity
* map.
*
* @return UserInterface
*
* @throws UnsupportedUserException if the user is not supported
*/
public function refreshUser(UserInterface $user)
{
// this is used for storing authentication in the session
// but in this case, the token is sent in each request,
// so authentication can be stateless. Throwing this exception
// is proper to make things stateless
throw new UnsupportedUserException();
}
/**
* Whether this provider supports the given user class.
*
* @param string $class
*
* @return bool
*/
public function supportsClass($class)
{
return User::class === $class;
}
}
我的ApiKeyAuthenticator
班级如下所示:
class ApiKeyAuthenticator implements SimplePreAuthenticatorInterface
{
public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
{
if (!$userProvider instanceof ApiKeyUserProvider) {
throw new \InvalidArgumentException(
sprintf(
'The user provider must be an instance of ApiKeyUserProvider (%s was given).',
get_class($userProvider)
)
);
}
$apiKey = $token->getCredentials();
$username = $userProvider->getUsernameForApiKey($apiKey);
if (!$username) {
// CAUTION: this message will be returned to the client
// (so don't put any un-trusted messages / error strings here)
throw new CustomUserMessageAuthenticationException(
sprintf('API Key "%s" does not exist.', $apiKey)
);
}
$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;
}
public function createToken(Request $request, $providerKey)
{
// look for an apikey query parameter
// $apiKey = $request->query->get('apikey');
// or if you want to use an "apikey" header, then do something like this:
$apiKey = $request->headers->get('apikey');
if (!$apiKey) {
// throw new BadCredentialsException();
// or to just skip api key authentication
return null;
}
return new PreAuthenticatedToken(
'anon.',
$apiKey,
$providerKey
);
}
}