Перевод статьи подготовлен специально для курса «Symfony Framework».
Не секрет, что ACL (access control lists) могут быть достаточно сложны в использовании. Поскольку Symfony рекомендует избирателей (voters) в качестве альтернативы ACL, я недавно решил, что напишу свой собственный простой в использовании Symfony 5 бандл для управления списками контроля доступа (ACL) в моих приложениях.
полностью полагается на сущности и репозитории Doctrine, что означает, что разрешения просто хранятся в базе данных без привязки к архитектуре вашего приложения.
Тем не менее, вот как легко JWT-токены аутентифицируются и авторизуются в подписчике событий с помощью так называемых easy ACL-репозиториев.
Большая часть этого кода не требует объяснения, если вы опытный разработчик; в основном, если входящий токен доступа успешно декодирован, что означает, что данный пользователь аутентифицирован, код пытается выяснить, имеет ли он права для доступа к текущему маршруту.
Достаточно только двух easy ACL-репозиториев (
Теперь давайте посмотрим в чем же вся магия. В целом, все заключается в определении маршрутов вашего приложения:
А также разрешений:
Итак, теперь, если ваша схема базы данных обновлена:
Четыре пустые таблицы будут добавлены в вашу базу данных:
Эта четверка идет рука об руку со следующими сущностями:
И репозиториями:
Наконец, консольная команда
Консоль MySQL:
Концепция идентификаторов пользователей позволяет пакету вообще не вмешиваться в вашу базу данных, которая не изменяется им.
Как вы можете видеть, три
Для получения более подробной информации читайте документацию, которая проведет вас через процесс установки и настройки бандла easy ACL.
На этом все. Был ли этот пост полезен? Я надеюсь, что да. Расскажите нам в комментариях ниже!
Symfony. Быстрый старт
Не секрет, что ACL (access control lists) могут быть достаточно сложны в использовании. Поскольку Symfony рекомендует избирателей (voters) в качестве альтернативы ACL, я недавно решил, что напишу свой собственный простой в использовании Symfony 5 бандл для управления списками контроля доступа (ACL) в моих приложениях.
programarivm/easy-acl-bundle
изначально был написан для использования в JWT-аутентифицированном API для одностраничных приложений (single page applications — SPA), но он также может быть полезен в ряде других сценариев, когда не требуется Security компонент — что в большинстве случаев, по моему скромному мнению, особенно подходит для сеансов обработки многостраничных приложений (multi-page applications — MPA).EasyAclBundle
полностью полагается на сущности и репозитории Doctrine, что означает, что разрешения просто хранятся в базе данных без привязки к архитектуре вашего приложения.
Тем не менее, вот как легко JWT-токены аутентифицируются и авторизуются в подписчике событий с помощью так называемых easy ACL-репозиториев.
// src/EventSubscriber/TokenSubscriber.php
namespace App\EventSubscriber;
use App\Controller\AccessTokenController;
use Doctrine\ORM\EntityManagerInterface;
use Firebase\JWT\JWT;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\KernelEvents;
class TokenSubscriber implements EventSubscriberInterface
{
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function onKernelController(ControllerEvent $event)
{
$controller = $event->getController();
// когда класс контроллера определяет несколько action методов, контроллер
// возвращается как [$controllerInstance, 'methodName']
if (is_array($controller)) {
$controller = $controller[0];
}
if ($controller instanceof AccessTokenController) {
$jwt = substr($event->getRequest()->headers->get('Authorization'), 7);
try {
$decoded = JWT::decode($jwt, getenv('JWT_SECRET'), ['HS256']);
} catch (\Exception $e) {
throw new AccessDeniedHttpException('Whoops! Access denied.');
}
$user = $this->em->getRepository('App:User')
->findOneBy(['id' => $decoded->sub]);
$identity = $this->em->getRepository('EasyAclBundle:Identity')
->findBy(['user' => $user]);
$rolename = $identity[0]->getRole()->getName();
$routename = $event->getRequest()->get('_route');
$isAllowed = $this->em->getRepository('EasyAclBundle:Permission')
->isAllowed($rolename, $routename);
if (!$isAllowed) {
throw new AccessDeniedHttpException('Whoops! Access denied.');
}
}
}
public static function getSubscribedEvents()
{
return [
KernelEvents::CONTROLLER => 'onKernelController',
];
}
}
Большая часть этого кода не требует объяснения, если вы опытный разработчик; в основном, если входящий токен доступа успешно декодирован, что означает, что данный пользователь аутентифицирован, код пытается выяснить, имеет ли он права для доступа к текущему маршруту.
...
$user = $this->em->getRepository('App:User')
->findOneBy(['id' => $decoded->sub]);
$identity = $this->em->getRepository('EasyAclBundle:Identity')
->findBy(['user' => $user]);
$rolename = $identity[0]->getRole()->getName();
$routename = $event->getRequest()->get('_route');
$isAllowed = $this->em->getRepository('EasyAclBundle:Permission')
->isAllowed($rolename, $routename);
...
Достаточно только двух easy ACL-репозиториев (
Identity
и Permission
) для определения того, может ли пользователь получить доступ к текущему маршруту.Конфигурация
Теперь давайте посмотрим в чем же вся магия. В целом, все заключается в определении маршрутов вашего приложения:
# config/routes.yaml
api_post_create:
path: /api/posts
controller: App\Controller\Post\CreateController::index
methods: POST
api_post_delete:
path: /api/posts/{id}
controller: App\Controller\Post\DeleteController::index
methods: DELETE
api_post_edit:
path: /api/posts/{id}
controller: App\Controller\Post\EditController::index
methods: PUT
А также разрешений:
# config/packages/programarivm_easy_acl.yaml
programarivm_easy_acl:
target: App\Entity\User
permission:
-
role: Superadmin
routes:
- api_post_create
- api_post_delete
- api_post_edit
-
role: Admin
routes:
- api_post_create
- api_post_edit
-
role: Basic
routes:
- api_post_create
Итак, теперь, если ваша схема базы данных обновлена:
php bin/console doctrine:schema:update --force
Четыре пустые таблицы будут добавлены в вашу базу данных:
easy_acl_identity
easy_acl_permission
easy_acl_role
easy_acl_route
Эта четверка идет рука об руку со следующими сущностями:
Programarivm\EasyAclBundle\Entity\Identity
Programarivm\EasyAclBundle\Entity\Permission
Programarivm\EasyAclBundle\Entity\Role
Programarivm\EasyAclBundle\Entity\Route
И репозиториями:
Programarivm\EasyAclBundle\Repository\IdentityRepository
Programarivm\EasyAclBundle\Repository\PermissionRepository
Programarivm\EasyAclBundle\Repository\RoleRepository
Programarivm\EasyAclBundle\Repository\RouteRepository
Наконец, консольная команда
easy-acl:setup
предназначена для заполнения таблиц easy ACL.php bin/console easy-acl:setup
This will reset the ACL. Are you sure to continue? (y) y
Консоль MySQL:
mysql> select * from easy_acl_identity;
Empty set (0.01 sec)
mysql> select * from easy_acl_permission;
+----+------------+-----------------+
| id | rolename | routename |
+----+------------+-----------------+
| 1 | Superadmin | api_post_create |
| 2 | Superadmin | api_post_delete |
| 3 | Superadmin | api_post_edit |
| 4 | Admin | api_post_create |
| 5 | Admin | api_post_edit |
| 6 | Basic | api_post_create |
+----+------------+-----------------+
6 rows in set (0.00 sec)
mysql> select * from easy_acl_role;
+----+------------+
| id | name |
+----+------------+
| 1 | Superadmin |
| 2 | Admin |
| 3 | Basic |
+----+------------+
3 rows in set (0.00 sec)
mysql> select * from easy_acl_route;
+----+-----------------+---------+-----------------+
| id | name | methods | path |
+----+-----------------+---------+-----------------+
| 1 | api_post_create | POST | /api/posts |
| 2 | api_post_delete | DELETE | /api/posts/{id} |
| 3 | api_post_edit | PUT | /api/posts/{id} |
+----+-----------------+---------+-----------------+
3 rows in set (0.00 sec)
Добавление идентификаторов пользователей
Концепция идентификаторов пользователей позволяет пакету вообще не вмешиваться в вашу базу данных, которая не изменяется им.
Как вы можете видеть, три
EasyAcl
таблицы заполнены данными, но, конечно же, это ваша задача — динамически определять идентификационные данные своих пользователей, как в примере, показанном ниже.// src/DataFixtures/EasyAcl/IdentityFixtures.php
namespace App\DataFixtures\EasyAcl;
use App\DataFixtures\UserFixtures;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Programarivm\EasyAclBundle\EasyAcl;
use Programarivm\EasyAclBundle\Entity\Identity;
class IdentityFixtures extends Fixture implements FixtureGroupInterface, DependentFixtureInterface
{
private $easyAcl;
public function __construct(EasyAcl $easyAcl)
{
$this->easyAcl = $easyAcl;
}
public function load(ObjectManager $manager)
{
for ($i = 0; $i < UserFixtures::N; $i++) {
$index = rand(0, count($this->easyAcl->getPermission())-1);
$user = $this->getReference("user-$i");
$role = $this->getReference("role-$index");
$manager->persist(
(new Identity())
->setUser($user)
->setRole($role)
);
}
$manager->flush();
}
public static function getGroups(): array
{
return [
'easy-acl',
];
}
public function getDependencies(): array
{
return [
RoleFixtures::class,
UserFixtures::class,
];
}
}
Для получения более подробной информации читайте документацию, которая проведет вас через процесс установки и настройки бандла easy ACL.
На этом все. Был ли этот пост полезен? Я надеюсь, что да. Расскажите нам в комментариях ниже!
Возможно, вас также заинтересует...
- Запись CASL React Abilities в JSON-файл с помощью команды Laravel Artisan.
- Сеанс SPA GUI как Non-HttpOnly Cookie
- Совет для ленивых разработчиков Symfony.
Symfony. Быстрый старт
CrazyLazy
Я как то не увидел разницы здесь… https://habr.com/ru/company/otus/blog/508424/