Дата обновления перевода: 2021-05-13

Авторизация

Когда любой из поставщикой аутентификации (см. Поставщики аутентификации) подтвердил все-еще-неавторизованный токен, будет возващён аутентифицированный токен. Слушатель аутентификации установит этот токен напрямую в TokenStorageInterface, используя его метод setToken().

С этого момента пользователь будет аутентифицирован, т.е. идентифицирован. Теперь, другие части приложения могут использовать токен, чтобы решить, может ли пользователь запрашивать определённый URI или изменять определённый объект. Это решение будет принято экземпляром AccessDecisionManagerInterface.

Решение авторизации будет всегда основываться на нескольких вещах:

  • Текущем токене
    Например, метод токена getRoleNames() может быть использован для извлечения ролей текущего пользователя (например, ROLE_SUPER_ADMIN), или решение может быть основано на классе токена.
  • Наборе атрибутов
    Каждый атрибут представляет определённое право, которое должен иметь пользователь, к примеру ROLE_ADMIN, чтобы гарантировать, что пользователь является администратором.
  • Объекте (опционально)
    Любой объект, для которого нужно проверять контроль доступа, вроде объекта статьи или комментария.

Доступ к менеджеру решений

Так как принятие решения о том, авторизирован ли пользователь производить определённое действие, может быть достаточно сложным процессом, сам стандартный AccessDecisionManager зависит от нескольких избирателей и выносит финальный вердикт, основываясь на всех голосах (положительных, отрицательных или нейтральных), который он получил. Он признаёт несколько стратегий:

affirmative (по умолчанию)
предоставлять доступ, как только есть один избиратель, предоставляющий доступ;
consensus
предоставлять доступ, если больше избирателей предоставляют доступ, чем отказывают в нём;
unanimous
предоставлять доступ только, если ни один из избирателей не отказал в нём. Если все избиратели воздержались от голосования, решение основывает на опции конфигурации allow_if_all_abstain (по умолчанию false).
priority

предоставляет или отказывает в доступе первому избирателю, который не воздреживается;

New in version 5.1: Версия стратегии priority была представлена в Symfony 5.1.

Применение доступных опций в деталях:

use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;

// экземпляр Symfony\Component\Security\Core\Authorization\Voter\VoterInterface
$voters = [...];

// одно из "affirmative", "consensus", "unanimous", "priority"
$strategy = ...;

// предоставлять доступ или нет, если все избиратели воздержались
$allowIfAllAbstainDecisions = ...;

// предоставлять доступ или нет, при отсутствии большинства (применимо только к стратегии "consensus")
$allowIfEqualGrantedDeniedDecisions = ...;

$accessDecisionManager = new AccessDecisionManager(
    $voters,
    $strategy,
    $allowIfAllAbstainDecisions,
    $allowIfEqualGrantedDeniedDecisions
);

See also

Вы можете изменить стратегию по умолчанию в конфигурации.

Избиратели

Избиратели являются экземплярами класса VoterInterface, что означает, что они должны реализовывать несколько методов, что позволяет менеджеру решений использовать их:

vote(TokenInterface $token, $object, array $attributes)
этот метод будет проводить голосование и возвращать значение, равное одной из констант класса VoterInterface, т.е. VoterInterface::ACCESS_GRANTED, VoterInterface::ACCESS_DENIED или VoterInterface::ACCESS_ABSTAIN;

Компонент Security содержит несколько стандартных избирателей, которые охватывают множество случаев применения:

AuthenticatedVoter

Избиратель AuthenticatedVoter поддерживает атрибуты IS_AUTHENTICATED_FULLY, IS_AUTHENTICATED_REMEMBERED, и IS_AUTHENTICATED_ANONYMOUSLY и предоставляет доступ, основываясь на текущем уровне аутентификации, т.е. является ли пользователь полностью аутентифицированным, или только за счёт куки “запомнить меня”, или вообще аутентифицирован анонимно?

Он также поддерживает атрибуты IS_ANONYMOUS, IS_REMEMBERED, IS_IMPERSONATOR для предоставляения доступа, основываясь на конкретном состоянии аутентификации.

New in version 5.1: Атрибуты IS_ANONYMOUS, IS_REMEMBERED и IS_IMPERSONATOR были представлены в Symfony 5.1.

use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver;

$trustResolver = new AuthenticationTrustResolver();

$authenticatedVoter = new AuthenticatedVoter($trustResolver);

// instance of Symfony\Component\Security\Core\Authentication\Token\TokenInterface
$token = ...;

// any object
$object = ...;

$vote = $authenticatedVoter->vote($token, $object, ['IS_AUTHENTICATED_FULLY']);

RoleVoter

RoleVoter поддерживает атрибуты, начинающиеся с ROLE_ и предоставляет доступ пользователю, если все обязательные атрибуты ROLE_* могут быть найдены в массиве ролей, возвращённых методом токена getRoleNames():

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_ADMIN, а также пользователям, которые имеют роль ROLE_SUPER_ADMIN:

use Symfony\Component\Security\Core\Authorization\Voter\RoleHierarchyVoter;
use Symfony\Component\Security\Core\Role\RoleHierarchy;

$hierarchy = [
    'ROLE_SUPER_ADMIN' => ['ROLE_ADMIN', 'ROLE_USER'],
];

$roleHierarchy = new RoleHierarchy($hierarchy);

$roleHierarchyVoter = new RoleHierarchyVoter($roleHierarchy);

ExpressionVoter

ExpressionVoter предоставляет доступ, основываясь на оценке выражений, созданных с помощью компонента ExpressionLanguage. Эти выражения имеют доступ к ряду специальных переменных безопасности:

use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\Security\Core\Authorization\Voter\ExpressionVoter;

// Symfony\Component\Security\Core\Authorization\ExpressionLanguage;
$expressionLanguage = ...;

// экземпляр Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface
$trustResolver = ...;

// Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface
$authorizationChecker = ...;

$expressionVoter = new ExpressionVoter($expressionLanguage, $trustResolver, $authorizationChecker);

// экземпляр Symfony\Component\Security\Core\Authentication\Token\TokenInterface
$token = ...;

// любой объект
$object = ...;

$expression = new Expression(
    '"ROLE_ADMIN" in role_names or (not is_anonymous() and user.isSuperAdmin())'
);

$vote = $expressionVoter->vote($token, $object, [$expression]);

Note

Когда вы создаете собственного избирателя, вы можете конечно же использовать его конструктор, чтобы внедрить любые зависимости, необходимые для принятия решения.

Роли

Роли - это строки, которые предоставляют выражения определённому праву пользователя (например, “редактировать пост в блоге”, “создать инвойс”). Вы можете свободно выбирать эти строки. Единственное требование - они должны начинаться с префикса

ROLE_ (например, ROLE_POST_EDIT, ROLE_INVOICE_CREATE).

Использование менеджера решений

Слушатель доступа

Менеджер решений доступа может быть использован в любой момент запроса, чтобы решить, имеет ли текущий пользователь право доступа к заданному источнику. Одним необязательным, но полезным методом для ограничения доступа, основываясь на схеме URL, является AccessListener, который является одним из слушателей брандмауэра (см. Слушатели брандмауэра), он вызывается для каждого запроса, совпадающего с картой брандмауэра (см. Брандмауэр для HTTP запросов).

Он использует карту доступа (которая должна быть экземпляром AccessMapInterface), содержащую сопоставиели запросов и соответствующий набор атрибутов, которые требуются для того, чтобы текущий пользователь получил доступ к приложению:

use Symfony\Component\HttpFoundation\RequestMatcher;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Http\AccessMap;
use Symfony\Component\Security\Http\Firewall\AccessListener;

$accessMap = new AccessMap();
$tokenStorage = new TokenStorage();
$requestMatcher = new RequestMatcher('^/admin');
$accessMap->add($requestMatcher, ['ROLE_ADMIN']);

$accessListener = new AccessListener(
    $tokenStorage,
    $accessDecisionManager,
    $accessMap,
    $authenticationManager
);

Проверщик авторизации

Менеджер решений доступа также доступен в других частях приложения через метод isGranted() класса AuthorizationChecker. Вызов этого метода напрямую делегирует вопрос менеджеру решений доступа:

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();
}

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.