В случае если любой из провайдеров аутентификации (см. Провайдеры аутентификации) подтвердит непроверенный токен, то будет возвращен аутентифицированный токен. Аутентификационный подписчик должен напрямую установить этот токен в TokenStorageInterface при помощи метода [setToken()](http://api.symfony.com/2.6/Symfony/Component/Security/Core/Authentication/Token/Storage/TokenStorageInterface.html#setToken()).
После этого пользователь считается аутентифицированным, то есть идентифицированным. Теперь в любой части приложения вы можете использовать этот токен для определения доступен ли определенный URL пользователю или может ли он изменять определенный объект. Само решение о праве доступа принимается экземпляром AccessDecisionManagerInterface.
Решение об авторизации всегда принимается основываясь на нескольких моментах:
- Текущий токен — Например, метод [getRoles()](http://api.symfony.com/2.6/Symfony/Component/Security/Core/Authentication/Token/TokenInterface.html#getRoles()) токена можно использовать для определения ролей пользователя (
ROLE_SUPER_ADMIN
), а сам класс токена можно использовать для принятия решения о аутентификации. - Набор атрибутов — каждый атрибут отвечает за определенное право, которым обладает пользователь, например,
ROLE_ADMIN
говорит о том, что пользователь является администратором. - Объект (опционально) — любой объект, доступ к которому определяется, например, объект статьи или комментария.
TokenStorageInterface
впервые был представлен в Symfony 2.6. В более ранних версиях приходилось использовать методsetToken()
изSecurityContextInterface
.
Менеджер доступа
Так как процесс принятия решения о наличии или отсутствии прав доступа для пользователя на операцию, может быть довольно сложным, стандартный AccessDecisionManager зависит от нескольких voter’ов, а окончательное решение принимается основываясь на всех voter’ах.
Существует несколько стратегий:
- affirmative (утвердительная, по-умолчанию) — доступ открывается сразу, как только один из voter’ов вернул положительный ответ
- consensus (согласование) — досутп открывается, если voter’ов с положительным ответом больше, чем с отрицательным
- (unanimous) единогласие — доступ открывается только в том случае, если все voter’ы дали утверждение
<?php
use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;
// instances of Symfony\Component\Security\Core\Authorization\Voter\VoterInterface
$voters = array(...);
// one of "affirmative", "consensus", "unanimous"
$strategy = ...;
// whether or not to grant access when all voters abstain
$allowIfAllAbstainDecisions = ...;
// whether or not to grant access when there is no majority (applies only to the "consensus" strategy)
$allowIfEqualGrantedDeniedDecisions = ...;
$accessDecisionManager = new AccessDecisionManager(
$voters,
$strategy,
$allowIfAllAbstainDecisions,
$allowIfEqualGrantedDeniedDecisions
);
Стратегию принятия решения можно изменить в конфигурации.
Voter’ы
Все voter’ы является экземплярами VoterInterface
, это означает, что каждый класс voter’а должен реализовать несколько методов для принятия решения:
supportsAttribute($attribute)
— используется для определения способен ли voter обработать атрибутsupportsClass($class)
— используется для определения может ли voter открыть или закрыть доступ к определенному классуvote(TokenInterface $token, $object, array $attributes)
— именно этот метод принимает решение об открытии или закрытии доступа и возвращает одну из следующих констант:VoterInterface::ACCESS_GRANTED
,VoterInterface::ACCESS_DENIED
илиVoterInterface::ACCESS_ABSTAIN
Компонент Security содержит в себе несколько стандартных voter’ов, которых должно быть достаточно в большинстве случаев:
AuthenticatedVoter
AuthenticatedVoter поддерживает атрибуты IS_AUTHENTICATED_FULLY
, IS_AUTHENTICATED_REMEMBERED
, и IS_AUTHENTICATED_ANONYMOUSLY
и открывает доступ в зависимости от текущего уровня аутентификации, то есть определяет аутентифицирован ли пользователь полностью или только при помощи cookie, или вообще аутентифицирован как аноним.
<?php
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver;
$anonymousClass = 'Symfony\Component\Security\Core\Authentication\Token\AnonymousToken';
$rememberMeClass = 'Symfony\Component\Security\Core\Authentication\Token\RememberMeToken';
$trustResolver = new AuthenticationTrustResolver($anonymousClass, $rememberMeClass);
$authenticatedVoter = new AuthenticatedVoter($trustResolver);
// instance of Symfony\Component\Security\Core\Authentication\Token\TokenInterface
$token = ...;
// any object
$object = ...;
$vote = $authenticatedVoter->vote($token, $object, array('IS_AUTHENTICATED_FULLY');
RoleVoter
RoleVoter
поддерживает атрибуты начинающиеся с ROLE_
и открывает доступ в том случае, если роль присутствует в массиве ролей пользователя, полученном при помощи метода getRoles()
:
<?php
use Symfony\Component\Security\Core\Authorization\Voter\RoleVoter;
$roleVoter = new RoleVoter('ROLE_');
$roleVoter->vote($token, $object, array('ROLE_ADMIN'));
RoleHierarchyVoter
RoleHierarchyVoter наследует RoleVoter и добавляет некоторый функционал к нему: он умеет обрабатывать иерархию ролей. Например, роль ROLE_SUPER_ADMIN
может содержать подроли ROLE_ADMIN
и ROLE_USER
, таким образом если некоторый объект требует наличия у пользователя роли ROLE_ADMIN
, то доступ будет открыт не только тому, кто имеет эту роль, но также и пользователю с ролью ROLE_SUPER_ADMIN
:
<?php
use Symfony\Component\Security\Core\Authorization\Voter\RoleHierarchyVoter;
use Symfony\Component\Security\Core\Role\RoleHierarchy;
$hierarchy = array(
'ROLE_SUPER_ADMIN' => array('ROLE_ADMIN', 'ROLE_USER'),
);
$roleHierarchy = new RoleHierarchy($hierarchy);
$roleHierarchyVoter = new RoleHierarchyVoter($roleHierarchy);
При создании своего voter’а вы, конечно, можете добавить в конструкторе необходимые вам зависимости.
Роли
Роли — объекты, отображающие определенный набор прав пользователя. Существует только одно правило — класс должен реализовать RoleInterface, то есть необходимо прописать методы [getRole()](http://api.symfony.com/2.6/Symfony/Component/Security/Core/Role/Role/RoleInterface.html#getRole()), который должен вернуть роль в строковом виде. Стандартный класс Role просто возвращает первый аргумент конструктора.
<?php
use Symfony\Component\Security\Core\Role\Role;
$role = new Role('ROLE_ADMIN');
// will echo 'ROLE_ADMIN'
echo $role->getRole();
Большинство токенов наследуют AbstractToken
, таким образом роли переданные конструктору будут автоматически преобразованы из строк в объекты класса Role.
Использование менеджера доступа
Слушатели доступа (The Access Listener)
Менеджер доступа может быть использован в любой точке запроса для определения наличия прав у пользователя. Один необязательный, но полезный метод для ограничения доступа на основе шаблона URL — AccessListener, который по-сути является одим из нескольких подписчиков файрвола (см. Подписчики Файрвола). Он обрабатывает каждый запрос, сравнивая его с картой файрвола (см. Файрвол при работе с HTTP запросами).
Он использует карту доступа (должна реализовывать интерфейс AccessMapInterface), которая содержит в себе шаблоны запросов и соответствующий им набор атрибутов. Пользователь должен иметь иметь набор этих атрибутов для доступа к приложению.
<?php
use Symfony\Component\Security\Http\AccessMap;
use Symfony\Component\HttpFoundation\RequestMatcher;
use Symfony\Component\Security\Http\Firewall\AccessListener;
$accessMap = new AccessMap();
$requestMatcher = new RequestMatcher('^/admin');
$accessMap->add($requestMatcher, array('ROLE_ADMIN'));
$accessListener = new AccessListener(
$securityContext,
$accessDecisionManager,
$accessMap,
$authenticationManager
);
Проверка авторизации
Менеджер доступа доступен во всех частях приложения через метод [isGranted()](http://api.symfony.com/2.6/Symfony/Component/Security/Core/Authorization/AuthorizationChecker.html#isGranted()) класса AuthorizationChecker. Вызов этого метода напрямую передаёт ваш запрос менеджеру доступа:
<?php
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
$authorizationChecker = new AuthorizationChecker(
$tokenStorage,
$authenticationManager,
$accessDecisionManager
);
if (!$authorizationChecker->isGranted('ROLE_ADMIN')) {
throw new AccessDeniedException();
}