【问题标题】:Symfony 4: ROLE_USER doesn't inherit IS_AUTHENTICATED_FULLY and Voter throws AccessDeniedExceptionSymfony 4:ROLE_USER 不继承 IS_AUTHENTICATED_FULLY 并且 Voter 抛出 AccessDeniedException
【发布时间】:2021-04-26 01:51:46
【问题描述】:

我正在与许多古老的员工一起开展一个遗留项目。有大量使用非常罕见的动作。半年前我们从 Symfony 2.8 升级到 Symfony 4.4。在经理尝试使用现在返回 AccessDeniedException: Access Denied. 的旧操作之一之前,一切都运行良好

我检查了 Symfony 文档,对我来说一切似乎都很简单。

The documentation 说:

检查用户是否已登录 (IS_AUTHENTICATED_FULLY)

如果您只想检查用户是否登录(您不关心角色),您有两种选择。首先,如果您为每个用户分配了 ROLE_USER,则可以检查该角色。

app/config/security.yml有下一个配置:

security:
    access_decision_manager:
        strategy: unanimous
        allow_if_all_abstain: true

    encoders:
        FOS\UserBundle\Model\UserInterface:
            algorithm: sha512
            encode_as_base64: false
            iterations: 1

    role_hierarchy:
        ROLE_CUSTOMER_ADMIN: ROLE_USER
        ROLE_ADMIN: ROLE_USER
        ROLE_ADMIN_CAN_EDIT_PERMISSIONS: ROLE_ADMIN
        ROLE_SUPER_ADMIN: ROLE_ADMIN_CAN_EDIT_PERMISSIONS

    providers:
          fos_user:
              id: fos_user.user_provider.username

    firewalls:
        main:
            pattern: ^/
            form_login:
                provider: fos_user
                default_target_path: /user-post-login
                always_use_default_target_path: true
                login_path: user_security_login
                check_path: fos_user_security_check
            logout:
                path: /logout
                target: /login
                handlers: [mp.logout_handler]
                invalidate_session: false
            anonymous: ~
            remember_me:
                secret:      "%secret%"
                lifetime: 31536000 # 365 days in seconds
                path:     /
                domain:   ~ # Defaults to the current domain from $_SERVER

    access_control:
        - { path: ^/login, role: IS_AUTHENTICATED_ANONYMOUSLY }
        ...
        - { path: ^/api, role: IS_AUTHENTICATED_ANONYMOUSLY }
        ...
        - { path: ^/admin/select-customer-status, role: [ROLE_CUSTOMER_ADMIN, ROLE_SUPER_ADMIN] }
        ...
        - { path: ^/admin, role: ROLE_USER }

我当前的用户有角色ROLE_SUPER_ADMIN,根据role_hierarchy这个角色在祖先中有ROLE_USER,但是当我尝试打开http://localhost:8080/admin/select-customer-status/1我得到了这个Access Denied 异常。

我尝试调试并发现这个异常出现在Symfony\Component\Security\Http\Firewall\AccessListener

但真正的问题是Voter 正在检查IS_AUTHENTICATED_FULLY under the hood,但$attributes 中不存在这个。

另一个有趣的事情是,当我将此配置直接添加到 Action 时,它按预期工作并且没有抛出异常:

/**
 * @Route("/admin/update-user-permissions/{id}", name="update_user_permissions")
 * @Method({"POST"})
 * 
 * @IsGranted("ROLE_SUPER_ADMIN")
 * @IsGranted("ROLE_CUSTOMER_ADMIN")
 */

有人可以帮忙解决这种奇怪的行为吗?

附:有a similar question,但它适用于 Symfony 2,对我来说并不可靠。

【问题讨论】:

  • 只有当属性是IS_AUTHENTICATED_* 类型之一时,您链接的选民才会投票(否则继续并最终返回弃权,因为没有设置其他结果),所以它可能不是罪魁祸首。事实上,在开发模式下,分析器中有一个安全选项卡,显示所有投票者的结果。请检查哪个实际上拒绝访问。
  • 很可能不是,但是您是否尝试在您的access_control 中设置roles 而不是role?我在文档中看到的所有示例似乎都使用了roles,并且存在多个的事实可能会产生一些影响;o) 但是您的注释版本应该要求用户具有 both 角色。
  • @Jakumi,这里是安全标签的a screenshot。我不明白出了什么问题。
  • 我尝试将 role 更改为 roles 但这没有任何影响。
  • 您发布的屏幕截图似乎暗示,用户必须拥有 both 角色,而仅拥有这两个角色之一的用户并非如此。您要么以某种方式引入 or (如下面的答案),要么为这两个组声明一个共同角色,这两个组在其层次结构中都提供了该共同角色,并改为使用该共同角色。

标签: php symfony symfony4 fosuserbundle


【解决方案1】:

显然发生的是,所有提到的角色都是必需的,即 both ROLE_SUPER_ADMINROLE_CUSTOMER_ADMIN。据我了解您的问题描述,您的用户是任何一个,但不是两者。

您提供的screenshot 在底部显示了处理请求的访问管理器:

这清楚地表明,AuthenticatedVoter 不是问题,因为它弃权(因为它只对IS_AUTHENTICATED_* 角色(例如IS_AUTHENTICATED_FULLY)进行投票,这在its code 中也很容易看到。

但是,也很容易看出,两个角色是分开检查的(!)。 RoleHierarchyVoter 现在尝试查看是否授予ROLE_CUSTOMER_ADMIN,并且我假设您的用户拥有ROLE_SUPER_ADMIN,RoleHierarchyVoter 将通过使用层次结构将其扩展为ROLE_SUPER_ADMINROLE_ADMIN_CAN_EDIT_PERMISSIONSROLE_ADMIN 和@987654335 @。都不是ROLE_CUSTOMER_ADMIN

正如我在评论中所建议的,您可能希望选择添加一个 两个 扩展管理员角色都拥有的新角色。如果ROLE_ADMIN 不合适,可能是ROLE_EXTENDED_ADMIN 或其他东西。

ROLE_CUSTOMER_ADMIN: [ROLE_ADMIN, ROLE_EXTENDED_ADMIN]
ROLE_SUPER_ADMIN: [ROLE_ADMIN_CAN_EDIT_PERMISSIONS, ROLE_EXTENDED_ADMIN]

然后在访问控制中:

 - { path: ^/admin/select-customer-status, roles: ROLE_EXTENDED_ADMIN }

【讨论】:

  • 非常感谢!你让我看到了真正的问题。我已经在数据库中向用户添加了两个角色,就像 a:2:{i:0;s:19:"ROLE_CUSTOMER_ADMIN";i:1;s:16:"ROLE_SUPER_ADMIN";} 一样,它按预期工作。
  • @SerhiiPopov btw:角色很棘手,在用户登录时更改它们可能不起作用,因为角色在会话期间存储在会话中。重新登录将导致预期的结果。以防万一;o)
【解决方案2】:

你可以使用

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;

/**
 * @Security("has_role('ROLE_SUPER_ADMIN') or has_role('ROLE_CUSTOMER_ADMIN')")
 */

that's all 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-15
    • 2017-06-14
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多