質問1:あなただけAPIKEYでユーザーを認証する場合は、可能な限り最良の解決策は、独自のユーザー・プロバイダを実装するだろう
だから多分、私はこのようなものが必要。解決策はよくsymfonyのドキュメントにdecribedさ:http://symfony.com/doc/current/security/api_key_authentication.html
EDIT - あなたが必要な数のユーザープロバイダを持つことができ、1つが故障した場合、その後、別のプレイするなり - ここで説明https://symfony.com/doc/current/security/multiple_user_providers.html
ダウン以下でありますApiKeyAuthenticatorのコードで、トークンを取得し、ApiKeyUserProviderを呼び出して、そのユーザを検索/取得します。ユーザーが見つかった場合、Symfonyセキュリティに提供されます。 ApiKeyUserProviderはUserRepositoryをユーザ操作に必要とします。あなたが持っていると確信しています。
コードはテストされていないため、微調整が必要な場合があります。
だから、仕事に取得することができます:
のsrc/BackendBundle /セキュリティ/ ApiKeyAuthenticator.php
namespace BackendBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authentication\SimplePreAuthenticatorInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\SimplePreAuthenticatorInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
class ApiKeyAuthenticator implements SimplePreAuthenticatorInterface, AuthenticationFailureHandlerInterface
{
protected $httpUtils;
public function __construct(HttpUtils $httpUtils)
{
$this->httpUtils = $httpUtils;
}
public function createToken(Request $request, $providerKey)
{
//use this only if you want to limit apiKey authentication only for certain url
//$targetUrl = '/login/check';
//if (!$this->httpUtils->checkRequestPath($request, $targetUrl)) {
// return;
//}
// get an apikey from authentication request
$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) {
//You can return null just skip the authentication, so Symfony
// can fallback to another authentication method, if any.
return null;
//or you can return BadCredentialsException to fail the authentication
//throw new BadCredentialsException();
}
return new PreAuthenticatedToken(
'anon.',
$apiKey,
$providerKey
);
}
public function supportsToken(TokenInterface $token, $providerKey)
{
return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
}
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 onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
// this contains information about *why* authentication failed
// use it, or return your own message
return new JsonResponse(//$exception, 401);
}
}
のsrc/BackendBundle /セキュリティ/ ApiKeyUserProvider.php
namespace BackendBundle\Security;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use BackendBundle\Entity\User;
use BackendBundle\Entity\UserORMRepository;
class ApiKeyUserProvider implements UserProviderInterface
{
private $userRepository;
public function __construct(UserORMRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function getUsernameForApiKey($apiKey)
{
//use repository method for getting user from DB by API key
$user = $this->userRepository->...
if (!$user) {
throw new UsernameNotFoundException('User with provided apikey does not exist.');
}
return $username;
}
public function loadUserByUsername($username)
{
//use repository method for getting user from DB by username
$user = $this->userRepository->...
if (!$user) {
throw new UsernameNotFoundException(sprintf('User "%s" does not exist.', $username));
}
return $user;
}
public function refreshUser(UserInterface $user)
{
if (!$user instanceof User) {
throw new UnsupportedUserException(sprintf('Expected an instance of ..., but got "%s".', get_class($user)));
}
if (!$this->supportsClass(get_class($user))) {
throw new UnsupportedUserException(sprintf('Expected an instance of %s, but got "%s".', $this->userRepository->getClassName(), get_class($user)));
}
//use repository method for getting user from DB by ID
if (null === $reloadedUser = $this->userRepository->findUserById($user->getId())) {
throw new UsernameNotFoundException(sprintf('User with ID "%s" could not be reloaded.', $user->getId()));
}
return $reloadedUser;
}
public function supportsClass($class)
{
$userClass = $this->userRepository->getClassName();
return ($userClass === $class || is_subclass_of($class, $userClass));
}
}
サービスの定義:
services:
api_key_user_provider:
class: BackendBundle\Security\ApiKeyUserProvider
apikey_authenticator:
class: BackendBundle\Security\ApiKeyAuthenticator
arguments: ["@security.http_utils"]
public: false
そして最後に、セキュリティプロバイダの設定:
providers:
chain_provider:
chain:
providers: [api_key_user_provider, db_username]
api_key_user_provider:
id: api_key_user_provider
db_username:
entity:
class: BackendBundle:User
property: username
私はより多くのsymfonyのドキュメントを勉強することをお勧め、など非常に優れた認証プロセスの説明、ユーザエンティティ、ユーザーのプロバイダは、あり
質問2 :アクセス拒否されたイベントに対して異なるアクセスタイプを設定することができます。
namespace BackendBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface;
class AccessDeniedHandler implements AccessDeniedHandlerInterface
{
public function handle(Request $request, AccessDeniedException $accessDeniedException)
{
$route = $request->get('_route');
if ($route == 'api')) {
return new JsonResponse($content, 403);
} elseif ($route == 'backend')) {
return new Response($content, 403);
} else {
return new Response(null, 403);
}
}
}
私はapikey認証の例で詳細な情報を更新しました。コードはテストされていないので、微調整が必要な場合があります。 –