与选民一起进行symfony表演

问题描述 投票:0回答:2

在我的 symfony 应用程序中,我使用投票者来检查用户是否可以访问某些功能。

现在,我有一个导航栏,其中菜单根据这些权限显示或隐藏。

仅此导航栏即可在大约 2 秒内呈现。每个页面都添加了很多内容。 如果我注释掉导航栏的 render_controller,我会获得 2 秒的时间。如果我在每个选民 is_granted 方法的顶部返回 true,我会获得 1.5 秒的时间。

我该如何解决这个问题?使用缓存?不使用选民?简化我的选民?使用选民不是最佳实践吗?即使 symfony 必须根据所有属性列表检查属性?

我的导航栏生成器:

<?php

namespace AppBundle\Menu;

use AppBundle\Application\Core\ExplorerManager;
use AppBundle\Entity\User\Associate;
use AppBundle\Entity\User\Role;
use Symfony\Component\Routing\Router;
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
use Symfony\Component\Translation\Translator;

class MenuBuilder
{
    /**
     * @var Router
     */
    private $router;
    /**
     * @var Translator
     */
    private $translator;
    /**
     * @var AuthorizationChecker
     */
    private $authorizationChecker;

    /**
     * @param Router $router
     * @param Translator $translator
     * @param AuthorizationChecker $authorizationChecker
     */
    public function __construct(Router $router, Translator $translator, AuthorizationChecker $authorizationChecker)
    {
        $this->router = $router;
        $this->translator = $translator;
        $this->authorizationChecker = $authorizationChecker;
    }

    public function getFrontendHeaderMenuItems($isAuthenticated)
    {
        $menuItems = array(
            array(
                'title' => 'pricing',
                'route' => 'cms',
                'route_parameters' => array('dir' => 'general', 'page' => 'foodmeup_premium')
            ),
//            array(
//                'title' => 'recipes',
//                'route' => 'explore',
//                'route_parameters' => array('object' => ExplorerManager::RECETTE)
//            ),
            array(
                'title' => 'blog',
                'route' => 'explore',
                'route_parameters' => array('object' => ExplorerManager::BLOG)
            ),
            array(
                'title' => 'contact',
                'route' => 'message_foodmeup'
            )
        );

        if (false == $isAuthenticated) {
            $menuItems[] = array(
                'title' => 'login',
                'route' => 'login'
            );
        }

        $menuItems = $this->generateMenuRoutes(
            $menuItems,
            'frontend_bars',
            array()
        );

        return $menuItems;
    }

    public function getBackendLeftSidebarMenuItems()
    {
        $menuItems = array(
            array(
                'title' => 'dashboard',
                'icon' => 'th-large',
                'route' => 'dashboard'
            ),
            array(
                'title' => 'administration',
                'icon' => 'star',
                'is_granted' => Role::ROLE_ADMIN,
                'children' => array(
                    array(
                        'title' => 'dashboard',
                        'route' => 'admin_dashboard',
                    ),
                    array(
                        'title' => 'moderation',
                        'route' => 'moderate_posts',
                    ),
                    array(
                        'title' => 'users',
                        'route' => 'switch_user',
                        'is_granted' => Role::ROLE_SUPERADMIN,
                    ),
                    array(
                        'title' => 'subscriptions',
                        'route' => 'grant_partner_subscription',
                        'is_granted' => Role::ROLE_SUPERADMIN,
                    )
                )
            ), array(
                'title' => 'ingredients',
                'icon' => 'flask',
                'children' => array(
                    array(
                        'title' => 'explore_ingredients',
                        'route' => 'explore',
                        'route_parameters' => array('object' => ExplorerManager::CONSOMMABLE)
                    ),
                    array(
                        'title' => 'my_ingredients',
                        'route' => 'user_ingredients_display',
                        'is_granted' => Associate::READ_INGREDIENT
                    ), array(
                        'title' => 'create_import',
                        'route' => 'edit_ingredient',
                        'is_granted' => Associate::EDIT_INGREDIENT
                    ), array(
                        'title' => 'stock',
                        'route' => 'set_ingredient_stock',
                        'is_granted' => Associate::READ_STOCK,
                    ), array(
                        'title' => "buying_cost",
                        'route' => 'parameter_cost',
                        'is_granted' => Associate::READ_COST,
                    )
                )
            ), array(
                'title' => 'recipes',
                'icon' => 'birthday-cake',
                'children' => array(
                    array(
                        'title' => 'explore_recipes',
                        'route' => 'explore',
                        'route_parameters' => array('object' => ExplorerManager::RECETTE)
                    ),
                    array(
                        'title' => 'my_recipes',
                        'route' => 'user_recipes_display',
                        'is_granted' => Associate::READ_RECIPE
                    ), array(
                        'title' => 'create',
                        'route' => 'edit_recipe',
                        'is_granted' => Associate::EDIT_RECIPE
                    ), array(
                        'title' => 'print',
                        'route' => 'print_recipes',
                        'is_granted' => Associate::READ_RECIPE
                    )
                )
            ), array(
                'title' => 'plannings',
                'icon' => 'tasks',
                'is_granted' => array(Associate::READ_PLANNING, Associate::EDIT_PLANNING, Associate::DELETE_PLANNING),
                'children' => array(
                    array(
                        'title' => 'my_plannings',
                        'route' => 'display_plannings',
                        'is_granted' => Associate::READ_PLANNING
                    ), array(
                        'title' => 'my_models',
                        'route' => 'display_plannings',
                        'route_parameters' => array('isModel' => true),
                        'is_granted' => Associate::READ_PLANNING
                    ), array(
                        'title' => 'create_planning',
                        'route' => 'edit_planning',
                        'is_granted' => array(Associate::EDIT_PLANNING, Associate::CREATE_MODEL)
                    )
                )
            ), array(
                'title' => 'orders',
                'icon' => 'phone',
                'is_granted' => array(Associate::READ_ORDER, Associate::EDIT_ORDER, Associate::EDIT_ORDER, Associate::DELETE_ORDER),
                'children' => array(
                    array(
                        'title' => 'my_orders',
                        'route' => 'display_planning_orders',
                        'is_granted' => Associate::READ_ORDER
                    ), array(
                        'title' => 'automatic_order',
                        'route' => 'automatic_order',
                        'is_granted' => Associate::EDIT_ORDER
                    ), array(
                        'title' => 'edit_order',
                        'route' => 'edit_order',
                        'is_granted' => Associate::EDIT_ORDER
                    )
                )
            ), array(
                'title' => 'suppliers',
                'icon' => 'truck',
                'is_granted' => array(Associate::EDIT_SUPPLIER, Associate::READ_SUPPLIER, Associate::DELETE_SUPPLIER),
                'children' => array(
                    array(
                        'title' => 'my_suppliers',
                        'route' => 'display_suppliers',
                        'is_granted' => Associate::READ_SUPPLIER
                    ), array(
                        'title' => 'select',
                        'route' => 'user_supplier_select',
                        'is_granted' => Associate::EDIT_SUPPLIER
                    ), array(
                        'title' => 'create',
                        'route' => 'edit_organization',
                        'route_parameters' => array('category_slug' => 'fournisseur-de-consommables'),
                        'is_granted' => Associate::EDIT_SUPPLIER
                    )
                )
            ), array(
                'title' => 'teams',
                'icon' => 'users',
                'is_granted' => array(Associate::EDIT_TEAM, Associate::READ_TEAM, Associate::DELETE_TEAM, Associate::MANAGE_RIGHTS),
                'children' => array(
                    array(
                        'title' => 'my_teams',
                        'route' => 'teams_display',
                    ), array(
                        'title' => 'worker_parameter',
                        'route' => 'worker_parameter',
                        'is_granted' => Associate::EDIT_TEAM,
                    )
                )
            )
        );

        $menuItems = $this->generateMenuRoutes($menuItems, 'backend_bars', array());

        return $menuItems;
    }

    private function generateMenuRoutes(Array $menuItems, $domain, $isGranted)
    {
        foreach ($menuItems as $key => $menuItem) {
            if (array_key_exists('is_granted', $menuItems[$key])) {
                $rights = is_array($menuItems[$key]['is_granted']) ? $menuItems[$key]['is_granted'] : array($menuItems[$key]['is_granted']);

                $rights = array_map(function ($right) {
                    return $this->authorizationChecker->isGranted($right);
                }, $rights);

                if (in_array(true, $rights) == false) {
                    unset($menuItems[$key]);
                    continue;
                }
            }

            if (array_key_exists('route', $menuItems[$key])) {
                $menuItems[$key]['uri'] = $this->router->generate($menuItems[$key]['route'], array_key_exists('route_parameters', $menuItems[$key]) ? $menuItems[$key]['route_parameters'] : array());
            } else {
                $menuItems[$key]['uri'] = '#/';
            }

            $menuItems[$key]['title'] = $this->translator->trans($menuItems[$key]['title'], array(), $domain);
            if (array_key_exists('children', $menuItems[$key])) {
                $menuItems[$key]['children'] = $this->generateMenuRoutes($menuItems[$key]['children'], $domain, $isGranted);
            }
        }

        return $menuItems;
    }

    public function extractTitles(Array $menuItems)
    {
        $titles = array();

        foreach ($menuItems as $key => $menuItem) {
            $titles[] = $menuItems[$key]['title'];
            if (array_key_exists('children', $menuItems[$key])) {
                $titles = array_merge($titles, $this->extractTitles($menuItems[$key]['children']));
            }
        }

        return $titles;
    }
}
php performance symfony twig
2个回答
1
投票

选民存在性能缺陷。因此,如果您需要更高的性能,那么最好使用 ACL 或优化选民加载,因为 symfony 在执行每次安全检查时都会循环所有选民。

假设菜单中有 30 个链接,有 20 个投票者。当您在 1 个循环中检查对所有链接的访问时,symfony 将调用投票者 600 次(其中大多数将立即返回 Voter::ACCESS_ABSTAIN,但这仍然需要时间,在您的情况下需要太多时间)。


0
投票

更新: Symfony 添加了选民缓存,如果新函数supportsAttribute 或supportsType 返回 false 一次,则可以跳过调用不需要的选民。

https://symfony.com/blog/new-in-symfony-5-4-faster-security-voters

© www.soinside.com 2019 - 2024. All rights reserved.